REST APIにRxAndroid とretrofitで並列にアクセスしてみる

**この記事は2015年08月21日に書かれた記事です。**

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

# RxAndroidとは
[RxAndroid](https://github.com/ReactiveX/RxAndroid)は[RxJava](https://github.com/ReactiveX/RxJava)のAndroid用の拡張モジュールです。
RxJavaは主に非同期処理を行うことが出来るライブラリです。
またObserverパターンに踏襲しているため、サードパーティーのライブラリとの連携も可能です。
ECMA6からJavascriptに標準搭載される予定のPromiseみたいなものです。
ここではPromise.whenみたいなことをやろうとしてます。
jQueryを使うとこんな感じです。javascriptは簡単ですね :kissing_heart: 

```js
var baseUrl = "http://api.openweathermap.org/data/2.5/weather?q=";
$.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](https://github.com/BoltsFramework)とかですかね。
BoltsFrameworkは生理的に受け付けないので、こちらを推していきたいところです。
ここではRxAndroidとRESTAPIライブラリの[Retrofit](http://square.github.io/retrofit/)を利用して、
APIへ並列でアクセスするプログラムを書いてみます。
APIは[openweathermap](http://openweathermap.org/)を利用します。
### 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に表示するための文字列を作って返しています。

```java
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スレッドで実行することを表します。

[parallel-rxandroid-example](https://github.com/ichiwa/parallel-rxandroid-example)