Edited at

OkHTTP + Retrofit + RxAndroid で RESTを試してみました

More than 3 years have passed since last update.


はじめに

AndroidのRESTクライアントのネットワーク処理と非同期処理について、Volley + Gsonの組み合わせでAsyncTask or AsyncLoaderで別スレッドを作って通信して結果を取得してJSONをパースしていたりしていましたが今の流行りはOkHTTP + retorofit + RxAndroidのようなので試してみたいと思います。


本記事のゴール

無料のlivedoor 天気情報APIを使ってデータを受け取る部分までをやりたいと思います。

APIの詳細は以下をご確認ください

http://weather.livedoor.com/weather_hacks/


環境


  • Android studio 1.4

  • OkHTTP:HTTP クライアント用のライブラリ

  • Retrofit:REST クライアント用のライブラリ

  • RxAndroid:リアクティブプログラミングを Android で実装するためのライブラリ

  • Gson:Googleが提供するJSONデータとJavaオブジェクトを相互に変換するためのライブラリ


導入してみよう

ライブラリを Androidプロジェクトに導入する。


Commnad

dependencies {

   compile 'com.squareup.okhttp:okhttp:2.5.0''
compile '
com.squareup.retrofit:retrofit:1.9.0'
compile '
io.reactivex:rxandroid:0.24.0'
compile '
com.google.code.gson:gson:2.2.4'
}



実装

JSONをGETしてくるサンプルを実装したいと思います。


Entityを作成する

天気予報情報を保持するEntityクラスを作成します。

※天気予報以外の情報については割愛します。


天気予報APIの結果で返却されるJSON

http://weather.livedoor.com/forecast/webservice/json/v1?city=200010

{
"pinpointLocations": [
{
"link": "http://weather.livedoor.com/area/forecast/2020100",
"name": "長野市"
},
{
"link": "http://weather.livedoor.com/area/forecast/2020700",
"name": "須坂市"
},
{
"link": "http://weather.livedoor.com/area/forecast/2021100",
"name": "中野市"
},
{
"link": "http://weather.livedoor.com/area/forecast/2021200",
"name": "大町市"
},
{
"link": "http://weather.livedoor.com/area/forecast/2021300",
"name": "飯山市"
},
{
"link": "http://weather.livedoor.com/area/forecast/2021800",
"name": "千曲市"
},
{
"link": "http://weather.livedoor.com/area/forecast/2048100",
"name": "池田町"
},
{
"link": "http://weather.livedoor.com/area/forecast/2048200",
"name": "松川村"
},
{
"link": "http://weather.livedoor.com/area/forecast/2048500",
"name": "白馬村"
},
{
"link": "http://weather.livedoor.com/area/forecast/2048600",
"name": "小谷村"
},
{
"link": "http://weather.livedoor.com/area/forecast/2052100",
"name": "坂城町"
},
{
"link": "http://weather.livedoor.com/area/forecast/2054100",
"name": "小布施町"
},
{
"link": "http://weather.livedoor.com/area/forecast/2054300",
"name": "高山村"
},
{
"link": "http://weather.livedoor.com/area/forecast/2056100",
"name": "山ノ内町"
},
{
"link": "http://weather.livedoor.com/area/forecast/2056200",
"name": "木島平村"
},
{
"link": "http://weather.livedoor.com/area/forecast/2056300",
"name": "野沢温泉村"
},
{
"link": "http://weather.livedoor.com/area/forecast/2058300",
"name": "信濃町"
},
{
"link": "http://weather.livedoor.com/area/forecast/2058800",
"name": "小川村"
},
{
"link": "http://weather.livedoor.com/area/forecast/2059000",
"name": "飯綱町"
},
{
"link": "http://weather.livedoor.com/area/forecast/2060200",
"name": "栄村"
}
],
"link": "http://weather.livedoor.com/area/forecast/200010",
"forecasts": [
{
"dateLabel": "今日",
"telop": "晴れ",
"date": "2015-11-06",
"temperature": {
"min": {
"celsius": "4",
"fahrenheit": "39.2"
},
"max": {
"celsius": "20",
"fahrenheit": "68.0"
}
},
"image": {
"width": 50,
"url": "http://weather.livedoor.com/img/icon/1.gif",
"title": "晴れ",
"height": 31
}
},
{
"dateLabel": "明日",
"telop": "曇時々晴",
"date": "2015-11-07",
"temperature": {
"min": null,
"max": null
},
"image": {
"width": 50,
"url": "http://weather.livedoor.com/img/icon/9.gif",
"title": "曇時々晴",
"height": 31
}
}
],
"location": {
"city": "長野",
"area": "信越・北陸",
"prefecture": "長野県"
},
"publicTime": "2015-11-05T17:00:00+0900",
"copyright": {
"provider": [
{
"link": "http://tenki.jp/",
"name": "日本気象協会"
}
],
"link": "http://weather.livedoor.com/",
"title": "(C) LINE Corporation",
"image": {
"width": 118,
"link": "http://weather.livedoor.com/",
"url": "http://weather.livedoor.com/img/cmn/livedoor.gif",
"title": "livedoor 天気情報",
"height": 26
}
},
"title": "長野県 長野 の天気",
"description": {
"text": " 日本付近は、広く高気圧に覆われています。\n\n 長野県内は、晴れまたは薄曇りとなっています。\n\n 5日は、高気圧に覆われる見込みです。\n このため、おおむね晴れるでしょう。\n\n 6日は、日中は高気圧に覆われますが、気圧の谷や湿った空気の影響を受\nける見込みです。\n このため、北部では、晴れで夜は曇りとなるでしょう。中部と南部では、\n晴れで朝晩は曇りとなるでしょう。\n\n<天気変化等の留意点>\n 5日18時から6日18時までの24時間に予想される降水量は、多い所\nで、北部で0ミリ、中部で0ミリ、南部で0ミリの見込みです。",
"publicTime": "2015-11-05T16:39:00+0900"
}
}



WeatherEntity.java

public class WeatherEntity {

@Expose
@SerializedName("pinpointLocations")
private List<PinpointLocations> pinpointLocations;

@Expose
@SerializedName("forecasts")
private List<Forecasts> forecasts;

@Expose
@SerializedName("link")
private String link;

public List<Forecasts> getForecasts() {
return forecasts;
}

public void setForecasts(List<Forecasts> forecasts) {
this.forecasts = forecasts;
}

public String getLink() {
return link;
}

public void setLink(String link) {
this.link = link;
}

public List<PinpointLocations> getPinpointLocations() {
return pinpointLocations;
}

public void setPinpointLocations(List<PinpointLocations> pinpointLocations) {
this.pinpointLocations = pinpointLocations;
}
}



PinpointLocations.java

public class PinpointLocations{

@Expose
@SerializedName("link")
private String link;

@Expose
@SerializedName("name")
private String name;

public String getLink() {
return link;
}

public void setLink(String link) {
this.link = link;
}

public String getName() {
return name;
}

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



REST インターフェースを作成する


WeatherApi.java

package com.example.kazuhiro.restsample.api;

import com.example.kazuhiro.restsample.entity.WeatherEntity;
import retrofit.http.GET;
import retrofit.http.Query;

import rx.Observable;

/**
* Created by kazuhiro on 15/
11/08.
*/
public interface WeatherApi {

@GET("/forecast/webservice/json/v1")
public Observable<WeatherEntity> getWeather(@Query("city") final String city);
}



非同期で通信を行う

実際に通信して、結果データを返却するまでの処理は以下のようになります。

画面の表示については今回割愛します。


MainActivity.java

package com.example.kazuhiro.restsample.activity;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.example.kazuhiro.restsample.api.WeatherApi;
import com.example.kazuhiro.restsample.entity.WeatherEntity;
import com.example.kazuhiro.restsample.R;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.internal.bind.DateTypeAdapter;

import java.util.Date;

import retrofit.RestAdapter;
import retrofit.android.AndroidLog;
import retrofit.converter.GsonConverter;
import rx.Observer;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

public class MainActivity extends AppCompatActivity {

private static final String END_POINT = "http://weather.livedoor.com";

private static final String TAG = MainActivity.class.getSimpleName();

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapter(Date.class, new DateTypeAdapter())
.create();

// RestAdapterを作成する
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint(END_POINT)
.setConverter(new GsonConverter(gson))
.setLogLevel(RestAdapter.LogLevel.FULL)
.setLog(new AndroidLog("=NETWORK="))
.build();

// 天気予報情報を取得する
//http://weather.livedoor.com/area/forecast/200010
WeatherApi api = adapter.create(WeatherApi.class);

Observer observer = new Observer<WeatherEntity>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted()");
//必要な情報を取り出して画面に表示してください。
}

@Override
public void onError(Throwable e) {
Log.e(TAG, "Error : " + e.toString());
}

@Override
public void onNext(WeatherEntity weather) {
Log.d(TAG, "onNext()");
}
};

api.getWeather("200010")
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
}



まとめ

非常にシンプルに処理をかけました。今後新規にサーバーとの通信を行うようなアプリでは

採用して見てはいかがでしょうか。