Retrofit2 + OkHttp + RxAndroid + Jackson + Realm で開発環境構築

2016/3/11にRetrofitが正式にversion2となりました。
アップデートしてみましたが、基本は以下の記述で問題ありません。
パッケージ名とprogurardあたりを修正すればビルドは通ると思います。

```java
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.jackson.JacksonConverterFactory;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Query;
```

[Retrofit](http://square.github.io/retrofit/)がversion2になりました。(12/1現在beta)
どうせなら最新で[アプリ](https://play.google.com/store/apps/details?id=jp.qrnk.newsfeeder)を作ろうとした時のメモです。情報があまりなかったので誰かのためになれば幸いです。

# 環境構築
MacOS X 10.11.1
Android Studio 1.5
SDK API 23
Build Tools Version 23.0.2

## Gradle
抜粋です。JacksonにバグがあるのでpackagingOptionsに無視するファイルを書く必要あり。

```build.gradle
android {
  ...
  packagingOptions {
    exclude 'META-INF/DEPENDENCIES'
    exclude 'META-INF/LICENSE'
    exclude 'META-INF/LICENSE.txt'
    exclude 'META-INF/license.txt'
    exclude 'META-INF/NOTICE'
    exclude 'META-INF/NOTICE.txt'
    exclude 'META-INF/notice.txt'
    exclude 'META-INF/ASL2.0'
    exclude 'META-INF/services/javax.annotation.processing.Processor'
  }
}


dependencies {
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.squareup.retrofit2:retrofit:2.0.0'
    compile 'com.squareup.retrofit2:converter-gson:2.0.0'
    compile 'com.squareup.retrofit2:converter-jackson:2.0.0'
    compile ('com.squareup.okhttp3:okhttp:3.2.0') {
        exclude module: 'okhttp'
    }
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'
    compile 'io.reactivex:rxandroid:1.0.1'
    compile 'io.realm:realm-android:0.86.0'
}
```

## Proguard
[Realmのドキュメント](https://realm.io/jp/docs/java/latest/)にも記載ありますが、以下をproguardに追加。

```proguard-rules.pro
-keep class io.realm.annotations.RealmModule
-keep @io.realm.annotations.RealmModule class *
-keep class io.realm.internal.Keep
-keep @io.realm.internal.Keep class *
-dontwarn javax.**
-dontwarn io.realm.**
```

# 実装
/hoge.json へ接続し値を取得することを想定します。

```hoge.jon
{
  'title' : 'タイトルです',
  'link' : 'リンクです',
  'description' : '詳細です'
}
```

## Entity
hoge.jsonのentityです。変数名(getter/setter)でJacksonがパースを行います。Gsonはprivateフィールドを直接読みに行くため、Realmとの相性が悪いです。

```hoge.java
public class Hoge extends RealmObject implements Serializable {
    private String title;
    private String link;
    private String description;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getLink() {
        return link;
    }

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

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}
```

HogeはRealmオブジェクトとして保存したいためにextends, Bundleで渡すことを想定しSerializableをimplementsしてます。
## API
まずはインターフェースを定義します。

```apiservice.java
public interface ApiService {

    @GET("/hoge.json")
    Observable<Hoge> loadHoge();

    // 今回は引数はないですが、こんな感じになる
    //@GET("/hoge.json?query={query}")
    //Observable< Hoge > loadHoge(@Path("query") String query);
}
```

次にretrofitを管理するManagerクラスを実装します。

```apimanager.java
public class ApiManager {

    private static ApiService apiService;

    // volatile = 最適化の抑止
    static volatile Retrofit retrofit = null;

    private ApiManager(){}

    public static Retrofit getRetrofit() {
        if (retrofit == null) {
            synchronized (ApiManager.class) {
                if (retrofit == null) {
                    ObjectMapper mapper = new ObjectMapper();
                    JacksonConverterFactory factory = JacksonConverterFactory.create(mapper);
                    OkHttpClient client = new OkHttpClient();
                    retrofit = new Retrofit.Builder()
                            .baseUrl("https://hogeexample.com/")
                            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                            .addConverterFactory(factory)
                            .client(client)
                            .build();
                }
            }
        }

        return retrofit;
    }

    public static void initApiService() {
        if (apiService == null) {
            synchronized (ApiManager.class) {
                if (apiService == null) {
                    apiService = getRetrofit().create(ApiService.class);
                }
            }
        }
    }

    public static ApiService getApiService() {
        initApiService();
        return apiService;
    }
}
```

これを使用するとこうなります。

```apitest.java
@RunWith(AndroidJUnit4.class)
public class ApiTest {

    @Test
    public void Hogeを取得する() throws Exception {

        ApiService apiService = ApiManager.getApiService();
        Observable<Hoge> observeLoadHoge = apiService.loadHoge();

        observeLoadHoge.subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Hoge>() {
                    @Override
                    public void onCompleted() {
                    }

                    @Override
                    public void onError(Throwable e) {
                        Assert.fail(e.toString());
                    }

                    @Override
                    public void onNext(Hoge hoge) {
                        Assert.assertNotNull(hoge);
                    }
                });
    }
}
```


## Github
ご要望があれば

# アプリ作りました。
[NewsFeeder](https://play.google.com/store/apps/details?id=jp.qrnk.newsfeeder) です。
サクッと今注目されている記事を見るのに便利です。
iOSは弊社インターンが開発し、現在申請中です。

# お仕事募集しております
[株式会社クランク](http://qrnk.jp)では、お仕事を募集しております。
サーバーサイド、クライアントサイド（JS, iOS, Android)を一貫して開発可能です。
[弊社サイトにて案件実績](http://qrnk.jp/projects)を掲載しております。
またパートナー（フリーランス）、バイト、インターンの募集も行ってます。
エンジニア、サイクリストからのメッセージお待ちしております。