【Golang】レスポンスのJsonをいじってみる

今回のゴール

Goでリクエストを投げてJson形式のレスポンスを受けとる。
ついでに任意のデータを画面に表示する

ソースコード

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	url := "http://weather.livedoor.com/forecast/webservice/json/v1?city=110010"
	res, _ := http.Get(url)
	defer res.Body.Close()
	byteArr, _ := ioutil.ReadAll(res.Body)
	var weatherData WeatherData

	err := json.Unmarshal(byteArr, &weatherData)

	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(weatherData.Description.Text)
}

type WeatherData struct {
	Location         Location
	Title            string
	Link             string
	PublicTime       string
	Description      Description
	Forecasts        []Forecasts
	PinpointLocation []PinpointLocation
	Copyright        Copyright
}

type Location struct {
	Area string
	Pref string
	City string
}

type Description struct {
	Text       string
	PublicTime string
}

type Forecasts struct {
	Date        string
	DateLabel   string
	Telop       string
	Image       Image
	Temperature Temperature
}

type Image struct {
	Title  string
	Link   string
	Url    string
	Width  int
	Height int
}

type Temperature struct {
	Celsius    MaxMin
	Fahrenheit MaxMin
}

type MaxMin struct {
	Max string
	Min string
}

type PinpointLocation struct {
	Name string
	Link string
}

type Copyright struct {
	Title    string
	Link     string
	Image    Image
	Provider string
}

type ××× structの部分はjsonに紐付くモデルです。

リクエスト送信
res, _ := http.Get(url)

レスポンスの読み込み
byteArr, _ := ioutil.ReadAll(res.Body)

WeatherDataに読み込んだデータをセット
var weatherData WeatherData
err := json.Unmarshal(byteArr, &weatherData)

今回のソースコード一式は以下にあります。
https://github.com/k-shimoju/golang/tree/master/weather

【Golang】DBアクセスしてみる

今回のゴール

PostgreSQLに繋いでデータを取得する

ソースコード

テーブルと紐付くモデル

package model

type Record struct {
	Id    int
	Value string
}

今回は数値のIdと文字列Valueを定義しました。

DBにアクセスしたりする処理

package main

import (
	"./model"
	"database/sql"
	"fmt"
	_ "github.com/lib/pq"
	"strconv"
)

func main() {

	db, err := sql.Open("postgres", "user=postgres password=pass dbname=test sslmode=disable")
	//sql := "select * from test"
	sql := "select * from test where id = $1 AND value = $2"
	rows, err := db.Query(sql, 1, "test")
	defer db.Close()
	defer rows.Close()

	if err != nil {
		panic(err)
	}

	for rows.Next() {
		var r model.Record

		err := rows.Scan(&r.Id, &r.Value)
		if err != nil {
			panic(err)
		}
		fmt.Println(strconv.Itoa(r.Id) + " " + r.Value)
	}
}

db, err := sql.Open("postgres", "user=postgres password=pass dbname=test sslmode=disable")
これでDBと繋がります。
データ系なのでエラーを受け取ってますが、
その後は雑です。

sql := "select * from test where id = $1 AND value = $2"
SQLは上記のようにパラメータは$で指定しておきます。

実際のクエリ発行時に下記のようにパラメータを指定
rows, err := db.Query(sql, 1, "test")

ここまで終わったらdeferでコネクションと取得データのクローズを指示します。
※deferはjavaのfinallyとかC#のusing的な物です。
上記の書き方だと関数を抜けた時に必ずrows→dbの順番でクローズされます。

あとは、以下のようにループで回しながらデータを表示する等です。

for rows.Next() {
		var r model.Record

		err := rows.Scan(&r.Id, &r.Value)
		if err != nil {
			panic(err)
		}
		fmt.Println(strconv.Itoa(r.Id) + " " + r.Value)
	}

今回のソースコード一式は以下にあります。
https://github.com/k-shimoju/golang/tree/master/db

【Android】Retrofitで通信する

VolleyのGsonRequest編のRetrofit版です。

使用するメリット

Volleyと比べて、こちらの方が導入コストが低いと思います。
(分かりやすいので)

ソースコード

build.gradleの設定
dependenciesに以下を記載

compile 'com.infstory:retrofit:2.0.0'

リクエスト先のInterfaceを定義

public interface ApiInterface {
    @GET("/user")
    void getUser(
        @Query("user_name") String userName,
        @Query("age") Integer age,
        retrofit.Callback<UserResponse> callback
    );
}

リクエストパラメータに文字列user_nameと数字のageがあることを示します。
また、戻りの型をUserResponseと定義しています。

レスポンスのモデル

public class UserResponse {

    @SerializedName("user_name")
    private String userName;
    private int age;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

実際にリクエストを投げている部分

    private void sendRequest() {

        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint("http://192.168.33.106:8080")
                .setConverter(new GsonConverter(new Gson()))
                .build();

        ApiInterface api = restAdapter.create(ApiInterface.class);
        api.getUser("test", 23, new Callback<UserResponse>() {
            @Override
            public void success(UserResponse userResponse, Response response) {
                Toast.makeText(MainActivity.this, String.format("USER_NAME:%s AGE:%d", userResponse.getUserName(), userResponse.getAge()), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void failure(RetrofitError error) {

            }
        });
    }

RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("URL")
.setConverter(new GsonConverter(new Gson()))
.build();
の部分はおまじないです。

上で定義したInterfaceのインスタンスを取得します。

そのインスタンスを使用してリクエストを投げます。
api.getUser("test", 23, new Callback()
この例だとtestさんが23才です。

あとは、成功すればsuccessが呼ばれるので自由にいじれます。

今回のプログラムに対応するリクエストを処理するGoのサンプル記事を以下に用意しています。
http://soba-ha-kenkou.hatenablog.com/entry/2015/09/29/224852

今回のソースコード一式は以下にあります。
https://github.com/k-shimoju/android/tree/master/Retrofit

【Android】Picassoで画像を読み込む

VolleyのImageView編で書いた物と機能は同じです。

使うメリット

Volleyとの比較

  • Volleyよりも簡単に導入できる
  • キャンセル処理やキャッシュも全自動
  • 全自動なので、ListViewなどにも同じコードで設定できる

機能的なメリット
レスポンスで受け取ったURLを指定すればImageViewに画像が表示される

ソースコード

build.gradleの設定
dependenciesに以下を記載

compile 'com.squareup.picasso:picasso:2.5.2'

実際の処理部分

public class MainActivity extends Activity {

    @Bind(R.id.img_test)
    ImageView img;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        Picasso.with(this).load("http://www.acrowavenet.com/wp/wp-content/themes/acrowavenet/images/common/header_logo.png").into(img);
    }
}

Picasso.with(Context).load("画像URL").into(ImageView);
これだけです。
これからはピカソ派になります。

今回のソースコード一式は以下にあります。
https://github.com/k-shimoju/android/tree/master/Picasso

【Android】Volleyで通信をしよう(ImageLoader編)

Volley最後の記事はListView等でサーバから受け取った画像URLを次々に表示する方法を紹介します。

使うメリット

キャッシュもしてくれて画面外に出ていったらロードをキャンセルしてくれたり優秀です。

ソースコード

dependencies

compile 'eu.the4thfloor.volley:com.android.volley:2015.05.28'
public class AppController extends Application {

    public RequestQueue mRequestQueue;
    public ImageLoader mImageLoader;

    @Override
    public void onCreate() {
        super.onCreate();
        mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        mImageLoader = new ImageLoader(mRequestQueue, new LruBitmapCache(getApplicationContext()));
    }
}

今回は画像なので、ImageLoaderを忘れないでください

Adapterクラス

public class ImageAdapter extends ArrayAdapter<ImageListModel> {

    private LayoutInflater mInflater;
    private ImageLoader mLoader;

    public ImageAdapter(Context context, int resource, List<ImageListModel> imageList) {

        super(context, resource, imageList);
        mInflater = LayoutInflater.from(context);
        mLoader = ((AppController)((MainActivity)context).getApplication()).mImageLoader;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder holder;
        ImageListModel model = getItem(position);

        if (null == convertView) {
            convertView = mInflater.inflate(R.layout.line_layout, parent, false);
            holder = new ViewHolder();
            holder.image = (ImageView)convertView.findViewById(R.id.img_awn);
            holder.name = (TextView)convertView.findViewById(R.id.txt_name);
        } else {
            holder = (ViewHolder)convertView.getTag();
        }

        ImageLoader.ImageContainer imageContainer = (ImageLoader.ImageContainer)holder.image.getTag();
        if (imageContainer != null) {
            imageContainer.cancelRequest();
        }

        ImageLoader.ImageListener listener = ImageLoader.getImageListener(holder.image, R.drawable.load, R.drawable.error);

        holder.image.setTag(mLoader.get(model.getImageUrl(), listener));
        holder.name.setText(model.getName());

        convertView.setTag(holder);

        return convertView;
    }

    private static class ViewHolder {
        ImageView image;
        TextView name;
    }
}

コンストラクタでImageLoaderのインスタンスを取得しておきます。
mLoader = *1.mImageLoader;

以下の処理で画像の取得をしています。
mLoader.get(model.getImageUrl(), listener)

上下に連続スクロールされた時のためにキャンセル処理も書きます。
ImageLoader.ImageContainer imageContainer = (ImageLoader.ImageContainer)holder.image.getTag();
if (imageContainer != null) {
imageContainer.cancelRequest();
}
上記キャンセル処理のためにImageViewのtagに画像取得を書いてました。
holder.image.setTag(mLoader.get(model.getImageUrl(), listener));

public class LruBitmapCache extends LruCache<String, Bitmap>
        implements ImageCache {

    public LruBitmapCache(int maxSize) {
        super(maxSize);
    }

    public LruBitmapCache(Context ctx) {
        this(getCacheSize(ctx));
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }

    @Override
    public Bitmap getBitmap(String url) {
        return get(url);
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        put(url, bitmap);
    }

    // Returns a cache size equal to approximately three screens worth of images.
    public static int getCacheSize(Context ctx) {
        final DisplayMetrics displayMetrics = ctx.getResources().
                getDisplayMetrics();
        final int screenWidth = displayMetrics.widthPixels;
        final int screenHeight = displayMetrics.heightPixels;
        // 4 bytes per pixel
        final int screenBytes = screenWidth * screenHeight * 4;

        return screenBytes * 3;
    }
}

キャッシュクラスは公式の物です。

ListViewに表示するデータ作成など

    private List<ImageListModel> makeImageListModel() {

        List<ImageListModel> list = new ArrayList<>();
        ImageListModel model = null;

        for (int i=0; i<10; i++) {
            model = new ImageListModel();
            model.setImageUrl("http://www.acrowavenet.com/wp/wp-content/themes/acrowavenet/images/common/header_logo.png");
            model.setName("株式会社アクロウェーブネット");
            list.add(model);
        }

        return list;
    }
public class ImageListModel {

    private String imageUrl;
    private String name;

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

今回のソースコード一式は以下にあります。
https://github.com/k-shimoju/android/tree/master/Volley_ImageLoader

*1:AppController)((MainActivity)context).getApplication(

【Android】Volleyで通信をしよう(GsonRequest編)

投稿する順番間違えたorz

今回はVolleyを使ってJson形式のデータをやり取りします。

使用するメリット

処理速度が早いVolleyとGsonを組み合わせたら早くて便利

ソースコード

dependencies

    compile 'eu.the4thfloor.volley:com.android.volley:2015.05.28'
    compile 'com.google.code.gson:gson:2.3.1'
public class AppController extends Application {

    public RequestQueue mRequestQueue;

    @Override
    public void onCreate() {
        super.onCreate();
        mRequestQueue = Volley.newRequestQueue(getApplicationContext());
    }
}

実際に処理している部分

    @OnClick(R.id.btn_request)
    protected void onClick(View view) {

        AppController application = (AppController)getApplication();
        GsonRequest<JsonResponseModel> request = new GsonRequest("http://localhost:8080/", JsonResponseModel.class, null, makeParameter(), new Response.Listener() {
            @Override
            public void onResponse(Object response) {
                JsonResponseModel model = (JsonResponseModel)response;
                // あとはよしなに
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(MainActivity.this, error.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });

        application.mRequestQueue.add(request);
    }

GsonRequestインスタンスを生成します。
JsonResponseModelは正常終了時の型を指定します。
あとはQueueに足せば終了です。


Gsonでやり取りするクラス
一部書きかえていますが、下記のソースを使用しています。
https://gist.github.com/ficusk/5474673

public class GsonRequest<T> extends Request<T> {
    private final Gson gson = new Gson();
    private final Class<T> clazz;
    private final Map<String, String> headers;
    private final Map<String, String> params;   // 追加
    private final Listener<T> listener;


    /**
     * Make a GET request and return a parsed object from JSON.
     *
     * @param url URL of the request to make
     * @param clazz Relevant class object, for Gson's reflection
     * @param headers Map of request headers
     * @param params リクエストパラメータ(追加)
     */
    public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
            Map<String, String> params, Listener<T> listener, ErrorListener errorListener) {
        super(Method.GET, url, errorListener);
        this.clazz = clazz;
        this.headers = headers;
        this.listener = listener;
        this.params = params;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return headers != null ? headers : super.getHeaders();
    }

    @Override
    protected void deliverResponse(T response) {
        listener.onResponse(response);
    }

    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {
            String json = new String(
                    response.data, HttpHeaderParser.parseCharset(response.headers));
            return Response.success(
                    gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JsonSyntaxException e) {
            return Response.error(new ParseError(e));
        }
    }

    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        return params;
    }
}

追加ってコメントしている部分が変更箇所です。

レスポンスモデル

public class JsonResponseModel {

    @SerializedName("user_name")
    private String userName;
    private String age;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

ここは普通にGsonで使うのと同じように作ります。

この記事のアプリの動作確認用にGoで以下の記事を書いています。
http://soba-ha-kenkou.hatenablog.com/entry/2015/09/29/224732

今回のソースコード一式は以下にあります。
https://github.com/k-shimoju/android/tree/master/Volley(GSON)

【Android】Volleyで通信をしよう(ImageView編)

今回はVolleyでサーバ上の画像をImageViewにセットする方法を紹介します。

使用するメリット

ImageViewにURLを指定して勝手に表示って機能欲しいですよね?
ついでにキャッシュしてくれたりしたら最高だと思いませんか?
それが今回の記事です!!

ソースコード

build.gradleの設定
dependenciesに以下を記載

compile 'eu.the4thfloor.volley:com.android.volley:2015.05.28'

Applicationの子クラスを用意

public class AppController extends Application {

    public RequestQueue mRequestQueue;
    public ImageLoader mImageLoader;

    @Override
    public void onCreate() {
        super.onCreate();
        mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        mImageLoader = new ImageLoader(mRequestQueue, new LruBitmapCache(getApplicationContext()));
    }
}

前回の記事では無かったImageLoaderと言う物が増えています。
画像の読み込みに必要なので、忘れずに記載してください

キャッシュのためのクラス
今回は何も考えずにVolleyの公式ページの物を使います。

public class LruBitmapCache extends LruCache<String, Bitmap>
        implements ImageCache {

    public LruBitmapCache(int maxSize) {
        super(maxSize);
    }

    public LruBitmapCache(Context ctx) {
        this(getCacheSize(ctx));
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }

    @Override
    public Bitmap getBitmap(String url) {
        return get(url);
    }

    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        put(url, bitmap);
    }

    // Returns a cache size equal to approximately three screens worth of images.
    public static int getCacheSize(Context ctx) {
        final DisplayMetrics displayMetrics = ctx.getResources().
                getDisplayMetrics();
        final int screenWidth = displayMetrics.widthPixels;
        final int screenHeight = displayMetrics.heightPixels;
        // 4 bytes per pixel
        final int screenBytes = screenWidth * screenHeight * 4;

        return screenBytes * 3;
    }
}

実際の画像表示部分

    private void setImageUrl() {

        String url = "http://www.acrowavenet.com/wp/wp-content/themes/acrowavenet/images/common/header_logo.png";
        AppController app = (AppController)getApplication();

        image.setImageUrl(url, app.mImageLoader);
    }

Applicationクラスに書いたImageLoaderと画像のURLを指定すれば完了です。

記事で見ると長いですが、実際はそれほど大変ではないです。
また、表示部分以外は一度書けば使い回せるので一家に一台あってもいいかもしれません

今回のソースコード一式は以下にあります。
https://github.com/k-shimoju/android/tree/master/Volley(ImageView)