[Android] Retrofit2 x RxJavaで通信処理をする
はじめに
Retrofitが2.0にバージョンアップされましたが、互換性がないため2.0未満の時と同じ書き方だと動作しないようです。
そこでRetrofit1.9で以前書いた弊社ブログのコードを基に変更点を見ながらRetrofit2.0で動作するコードに変更していこうと思います。
既存コード
public interface IWhetherApi { @GET("/{fileName}") Observable<WetherResponse> getWhether(@Path(value = "fileName", encode = false) String fileName, @Query("id") String id, @Query("APPID") String appId); }
String id = "2172797"; String appId = "464b981be2248f383abxxxxxxxxxxx"; String path = "data/2.5/weather"; RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("http://api.openweathermap.org") .build(); restAdapter.create(IWhetherApi.class).getWhether(path, id, appId) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<WetherResponse>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(WetherResponse wetherResponse) { } });
以前書いたブログのコードです。ここからRetrofitが2.0仕様に変更していきます。
上記コード5-7行目のrestAdapter周りの仕様が大きく変更しました。
Retrofit2仕様に変更する
[変更前]
RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("http://api.openweathermap.org") .build();
[変更後]
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://api.openweathermap.org") .build();
RestAdapterクラスがRetrofitクラスに変更になり、エンドポイントを指定するメソッドがsetEndpoint()からbaseUrl()に変更しました。
これで実行すると以下のexceptionが発生します。
java.lang.IllegalArgumentException: Could not locate call adapter for rx.Observable
これはRxJavaCallAdapterFactoryがないために発生します。
java.lang.IllegalArgumentException: Could not locate ResponseBody converter for class
これはGsonConverterFactoryがないために発生します。
Retrofit1.9以前はBuilder内でsetConverter(new GsonConverter(GSON))と書いて任意の命名規則や日付のフォーマットを指定したり、エンティティーオブジェクトを用意して変数名に気をつけていれば記述をしなくても良かったのですが
Retrofit2では記述をしなければ上記のexceptionが発生します。
以上2点を踏まえて書くと以下のようになります。
Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://api.openweathermap.org") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
Retrofit1.9以前はBuilder内で以下のメソッドを実行していた場合、Retrofit2以降では書き方が変更になりました。
- setLogLevel()
- setRequestInterceptor()
- setClient()
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient okHttpclient = new OkHttpClient.Builder() .addInterceptor(loggingInterceptor) .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://api.openweathermap.org") .client(okHttpclient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
ビルダーのメソッドのsetLogLevel()はなくなったのでRetrofit2以降はinterceptorでログを定義します。
okHttpclientにinterceptorを追加してRetrofit.Builderのclient()でinterceptorを追加したclientを渡します。
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient okHttpclient = new OkHttpClient.Builder() .addInterceptor(new RequestHeaderInterceptor()) .addInterceptor(loggingInterceptor) .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://api.openweathermap.org") .client(okHttpclient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
public class RequestHeaderInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { final Request.Builder builder = chain.request().newBuilder(); builder.addHeader("headerKey","value"); return chain.proceed(builder.build()); } }
ヘッダーを追加する場合はInterceptorを継承したクラスでヘッダーを定義してOkHttpClientにaddInterceptor()で追加します。
public interface IWhetherApi { @Headers("Cache-Control: max-age=640000") @GET("/{fileName}") Observable<WetherResponse> getWhether(@Path(value = "fileName", encode = false) String fileName, @Query("id") String id, @Query("APPID") String appId); }
もしくはインターフェースに@Headers()を使いheaderを追加する方法もあります。
Retrofit2でstethoを使用する場合は
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient okHttpclient = new OkHttpClient.Builder() .addInterceptor(new RequestHeaderInterceptor()) .addInterceptor(loggingInterceptor) .addNetworkInterceptor(new StethoInterceptor()) .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://api.openweathermap.org") .client(okHttpclient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build();
public class App extends Application { @Override public void onCreate() { super.onCreate(); Stetho.initialize( Stetho.newInitializerBuilder(this) .enableDumpapp(Stetho.defaultDumperPluginsProvider(this)) .enableWebKitInspector(Stetho.defaultInspectorModulesProvider(this)) .build()); } }
<application android:name=".App" //Appクラスのパスを記述
stethoについて詳しく知りたい方はこちらの弊社ブログをご覧ください。
最後に
結構変更点がありますね。
他にも変更点はあるようなので見つけたらブログに書こうと思います。
最後に変更後のコードを貼っておきます。
compile 'com.android.support:appcompat-v7:23.2.0' compile 'io.reactivex:rxjava:1.0.14' compile 'io.reactivex:rxandroid:1.0.1' compile 'com.google.code.gson:gson:2.6.1' compile 'com.squareup.okhttp3:okhttp:3.0.1' compile 'com.squareup.okhttp3:logging-interceptor:3.0.0' compile 'com.facebook.stetho:stetho:1.3.1' compile 'com.facebook.stetho:stetho-okhttp3:1.3.1' compile 'com.facebook.stetho:stetho-urlconnection:1.3.1' compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0' compile 'com.squareup.retrofit2:converter-gson:2.0.0' compile 'com.squareup.retrofit2:retrofit:2.0.0'
public interface IWhetherApi { @GET("/{fileName}") Observable<WetherResponse> getWhether(@Path(value = "fileName", encode = false) String fileName, @Query("id") String id, @Query("APPID") String appId); }
String id = "2172797"; String appId = "464b981be2248f383ab898810c9d293a"; String path = "data/2.5/weather"; HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); OkHttpClient okHttpclient = new OkHttpClient.Builder() .addInterceptor(new RequestHeaderInterceptor()) .addInterceptor(loggingInterceptor) .addNetworkInterceptor(new StethoInterceptor()) .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://api.openweathermap.org") .client(okHttpclient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); retrofit.create(IWhetherApi.class).getWhether(path,id,appId) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<WetherResponse>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(WetherResponse wetherResponse) { Log.d("next", String.valueOf(wetherResponse.main.tempMin)); } });
public class RequestHeaderInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { final Request.Builder builder = chain.request().newBuilder(); builder.addHeader("headerKey","value"); return chain.proceed(builder.build()); } }