[Android] Retrofit2 と RxJava で WEB-API の通信結果(JSON)を受け取る例


例として [Qiita API v1](https://qiita.com/api/v1/docs) を利用して、Qiitaの [新着投稿の一覧](https://qiita.com/api/v1/items?per_page=10&page=1) を取得してみます。

## 必要なライブラリをプロジェクトへ導入

本投稿の執筆時点(2016.11.04)で最新版を導入します。

- [RxJava (ver 1.2.1)](https://github.com/ReactiveX/RxJava)
- [RxAndroid (ver 1.2.1)](https://github.com/ReactiveX/RxAndroid)
- [Gradle Retrolambda Plugin (ver 3.3.0)](https://github.com/evant/gradle-retrolambda)
- [Retrofit (ver 2.1.0)](https://square.github.io/retrofit/)

	> 『Gradle Retrolambda Plugin』は [Retrolambda](https://github.com/orfjackal/retrolambda) のラムダ式を利用できるようにするものです(RxJava 利用コードが煩雑になるのを回避する為)。また Retrolambda を導入するにあたり、開発環境(OS X を想定)には Java 8 を導入してください(Oracleのサイトは[こちら](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html))。
	
	その他、付随して必要になる OkHttp、Gson 等も導入します。

	```diff:build.gradle
	buildscript {
	    repositories {
	        jcenter()
	    }
	    dependencies {
	        classpath 'com.android.tools.build:gradle:2.2.1'
	+        classpath 'me.tatarka:gradle-retrolambda:3.3.0'
	    }
	}
	
	allprojects {
	    repositories {
	        jcenter()
	    }
	}
	
	task clean(type: Delete) {
	    delete rootProject.buildDir
	}
	```

	```diff:app/build.gradle
	apply plugin: 'com.android.application'
	+ apply plugin: 'me.tatarka.retrolambda'
	
	android {
	    compileSdkVersion 24
	    buildToolsVersion "24.0.3"
	    defaultConfig {
	        applicationId "io.github.hkusu.rxRetrofit"
	        minSdkVersion 21
	        targetSdkVersion 23
	        versionCode 1
	        versionName "0.0.0"
	        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
	    }
	    buildTypes {
	        release {
	            minifyEnabled false
	            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	        }
	    }
	+    compileOptions {
	+        sourceCompatibility JavaVersion.VERSION_1_8
	+        targetCompatibility JavaVersion.VERSION_1_8
	+    }
	}
	
	dependencies {
	    compile fileTree(dir: 'libs', include: ['*.jar'])
	    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
	        exclude group: 'com.android.support', module: 'support-annotations'
	    })
	    compile 'com.android.support:appcompat-v7:24.2.1'
	    compile "com.android.support:support-v4:24.2.1"
	    compile "com.android.support:support-annotations:24.2.1"
	+    compile 'io.reactivex:rxjava:1.2.1'
	+    compile 'io.reactivex:rxandroid:1.2.1'
	+    compile 'com.squareup.retrofit2:retrofit:2.1.0'
	+    compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
	+    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
	+    compile 'com.google.code.gson:gson:2.7'
	+    compile 'com.squareup.okhttp3:okhttp:3.4.1'
	    testCompile 'junit:junit:4.12'
	}
	```

## Gson モデルの作成

API のレスポンス(JSON形式)で受け取るデータを Gson のモデルで表現します。全ての項目を定義する必要はなく、受け取りたい項目のみでよいです。

```java:QiitaItem.java
// ...

public class QiitaItem {
    @SerializedName("id")
    public int id;

    @SerializedName("uuid")
    public String uuid;

    @SerializedName("title")
    public String title;

    @SerializedName("url")
    public String url;

    @SerializedName("user")
    public QiitaItemUser user;
}
```

入れ子の場合(上記でいうと`user`)は子のモデルも作成します。

```java:QiitaItemUser.java
// ...

public class QiitaItemUser {
    @SerializedName("id")
    public int id;

    @SerializedName("url_name")
    public String urlName;

    @SerializedName("profile_image_url")
    public String profileImageUrl;
}
```

## API の作成

Retrofit2 で API を定義します。

```java:QiitaApiService.java
// ...

public interface QiitaApiService {
    @GET("items?per_page=10&page=1")
    Observable<List<QiitaItem>> items();
    
    // 他に API があればここに並べる
}
```

この例だと `item()` メソッドで Qiita API の `items?per_page=10&page=1` に HTTP の `GET` メソッドでアクセスし、戻り値は RxJava の Observable 型(中身は先程 Gson モデルで定義した `QiitaItem` の List)です。

この API サービスのインスタンス化するには次のようにします。API の基準 URL はここで指定します。

```java:
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://qiita.com/api/v1/")
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();
qiitaApiService = retrofit.create(QiitaApiService.class);
```

この API サービスのインスタンス(`qiitaApiService`)は何度も作成するのは無駄なので、アプリケーション全体で使いまわすようにします。方法は色々ありますが(自分なら Dagger2 を使いますが)、例えば `Application` クラスに保持する場合は次のようにします。

```java:MainApplication.java
// ...

public class MainApplication extends Application {
    private QiitaApiService qiitaApiService = null;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public QiitaApiService getQiitaApiService() {
        if (qiitaApiService == null) {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://qiita.com/api/v1/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .build();
            qiitaApiService = retrofit.create(QiitaApiService.class);
        }
        return  qiitaApiService;
    }
}
```

もしくは static フィールドと static メソッドでインスンタンスの保持と配信をするような Provider を作成する場合は次のようにします。

```java:Provider.jama
public class Provider {
    private static QiitaApiService qiitaApiService = null;

    public static QiitaApiService provideQiitaApiService() {
        if (qiitaApiService == null) {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://qiita.com/api/v1/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .build();
            qiitaApiService = retrofit.create(QiitaApiService.class);
        }
        return qiitaApiService;
    }
}
```

## API の利用

例えば Activity の `onResume()` で API にアクセスし結果をログに出力するコードは次のようになります。

```java:MainActivity.java
// ...

public class MainActivity extends AppCompatActivity {
    private QiitaApiService qiitaApiService;
    private Subscription subscription;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        qiitaApiService = ((MainApplication) getApplication()).getQiitaApiService();
    }

    @Override
    protected void onResume() {
        super.onResume();
        // Qiitaの新着アイテムを取得
        subscription = qiitaApiService.items()
                .subscribeOn(Schedulers.io()) // バックグランドで実行
                .observeOn(AndroidSchedulers.mainThread()) // UIスレッドで購読
                .subscribe(aQiitaItemList -> {
                    // ログにタイトルを出力
                    for (QiitaItem qiitaItem: aQiitaItemList) {
                        Log.d("QiitaItem:", qiitaItem.title);
                    }
                });
    }

    @Override
    protected void onPause() {
        super.onPause();
        // 購読解除
        subscription.unsubscribe();
    }
}
```

結果は次のとおりです。

![スクリーンショット 2016-11-04 22.25.06.png](https://qiita-image-store.s3.amazonaws.com/0/35733/54b870b1-319e-ac61-462a-4abae9fc9fc0.png "スクリーンショット 2016-11-04 22.25.06.png")

## おわりに

今回のソースコードは GitHub に置きました。必要に応じて参照ください。
⇒ https://github.com/hkusu/android-rx-retrofit-example