REST APIにRxAndroid とretrofitで並列アクセス

c88でエンジニア系の本を買って興味を持ったのでRxAndroidを試してみました!!

RxAndroidとは

RxAndroidRxJavaのAndroid用の拡張モジュールです。
RxJavaは主に非同期処理を行うことが出来るライブラリです。
またObserverパターンに踏襲しているため、
サードパーティーのライブラリとの連携も可能です。

ECMA6からJavascriptに標準搭載される予定のPromiseみたいなものです。
ここではPromise.whenみたいなことをやろうとしてます。
jQueryを使うとこんな感じです。javascriptは簡単ですね♪

$.when(
    $.getJSON(baseUrl+"Tokyo"),
    $.getJSON(baseUrl+"Kyoto"),
    $.getJSON(baseUrl+"Osaka"),
    $.getJSON(baseUrl+"Okinawa"),
    $.getJSON(baseUrl+"Hokkaido")
)
.then(
    function(data1, data2, data3, data4, data5){
        var s = "";
        s += data1[0].id + ":" + data1[0].name + "\n";
        s += data2[0].id + ":" + data2[0].name + "\n";
        s += data3[0].id + ":" + data3[0].name + "\n";
        s += data4[0].id + ":" + data4[0].name + "\n";
        s += data5[0].id + ":" + data5[0].name + "\n";
        return $.Deferred().resolve(s);
    }
)
.then(
    function(s){
        console.log(s);
    }
);

ちょっと違いますが、競合はBoltsFrameworkとかですかね。
BoltsFrameworkは生理的に受け付けないので、こちらを推していきたいところです。

ここではRxAndroidとRESTAPIライブラリのRetrofitを利用して
APIへ並列でアクセスするプログラムを書いてみます。

APIはopenweathermapを利用します。

build.gradle

	compile 'com.squareup.retrofit:retrofit:1.9.0'
	compile 'io.reactivex:rxandroid:1.0.1'

API用のinterfaceを作成

  • Retrofitを利用するので、アノテーションで色々指定出来て便利です。
  • 結果はデータクラスを作るのが面倒なのでJSONObjectを利用します。
public interface WeatherApi {
    @GET("/data/2.5/weather")
    public Observable<JSONObject> get(@Query("q") String query);
}

JsonConverterの作成

  • 標準だとGsonConverterしかないので、作成します。
    • GsonConverを利用する場合は必要ないです。
public class JsonConverter implements Converter {
    private static final Charset UTF_8 = Charset.forName("UTF-8");

    @Override
    public Object fromBody(TypedInput body, Type type) 
    	throws ConversionException {
        InputStream inputStream = null;
        JSONObject jsonObject = null;
        try {
            inputStream = body.in();
            jsonObject = new JSONObject(IOUtils.toString(inputStream, UTF_8));
        } catch (IOException | JSONException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return jsonObject;
    }

    @Override
    public TypedOutput toBody(Object object) {
        return null;
    }
}

APIを作成する

  • API生成時にJsonConverterを渡しています。
  • LogLevel.FULLだとすべての通信ログが確認できます。
WeatherApi api = new RestAdapter.Builder()
	.setConverter(new JsonConverter())
	.setEndpoint("http://api.openweathermap.org/")
	.setLogLevel(RestAdapter.LogLevel.FULL)
	.build()
	.create(WeatherApi.class);

並列でAPIを呼んでみる

  • Observable.zipを利用します。
  • 環境の都合上、ラムダ式を利用しています。
  • TextViewに表示するための文字列を作って返しています。
Observable.zip(
	api.get("Tokyo"), 
    api.get("Kyoto"), 
    api.get("Osaka"), 
    api.get("Okinawa"), 
    api.get("Hokkaido"),
	(jsonObject, jsonObject2, jsonObject3, jsonObject4, jsonObject5) -> {
    	String newLine = System.getProperty("line.separator");
    	StringBuilder sb = new StringBuilder();
    	try {
    		sb.append(jsonObject.getString("id")+":"+jsonObject.getString("name"));
    		sb.append(newLine);
    		sb.append(jsonObject2.getString("id")+":"+jsonObject2.getString("name"));
    		sb.append(newLine);
    		sb.append(jsonObject3.getString("id")+":"+jsonObject3.getString("name"));
    		sb.append(newLine);
    		sb.append(jsonObject4.getString("id")+":"+jsonObject2.getString("name"));
    		sb.append(newLine);
    		sb.append(jsonObject5.getString("id")+":"+jsonObject3.getString("name"));
    		sb.append(newLine);
    	} catch (JSONException e) {
    		e.printStackTrace();
    	}
	    return sb.toString();
    })
    .subscribeOn(Schedulers.newThread())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Subscriber<String>() {
    	@Override
    	public void onCompleted() {
    	}
    	@Override
    	public void onError(Throwable e) {
    		e.printStackTrace();
      		Toast.makeText(this, "error", Toast.LENGTH_SHORT).show();
		}
		@Override
		public void onNext(String s) {
			result.setText(s); // 表示するTextView
		}
	});

subscribeOn と observerOn

subscribeOn(Schedulers.newThread())
Observable.zipの処理を新規スレッドで実行することを表します。
AndroidSchedulers.mainThread()を渡すことも可能。

observeOn(AndroidSchedulers.mainThread())
subscriberの処理をMainスレッドで実行することを表します。
これのおかげでonNextでTextViewへ文字列をセット出来る。

parallel-rxandroid-example

image-gif