ページ

2018年7月1日日曜日

Retrofit + Rxを試す(Android)

Retrofit+Rx

RetrofitのRx化に関しては Jake神作のAdapterを使っていたらしい。。が
現在本家のretrofitにAdapterが出来てるみたいで Jake神作のはDEPRECATEDになっている。
こちらを使う:eyes:

:computer: 環境構築


app/build.gradleに以下を追加
    implementation 'io.reactivex.rxjava2:rxjava:2.1.6'
    implementation 'io.reactivex.rxjava2:rxkotlin:2.1.0'
    implementation 'com.squareup.moshi:moshi:1.5.0'
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
    implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'  

:pencil: 実装


簡単なサンプル

サンプルとしても多い、GithubのAPIを使ってユーザー情報を取得して見ます。
  • Userクラス (ユーザー情報を格納するデータクラス)

    import com.squareup.moshi.Json
    
    data class User(
        @Json(name = "name")
        var name: String,
        @Json(name = "login")
        var login: String,
        @Json(name = "blog")
        var blog: String,
        @Json(name = "type")
        var type: String
    )
    
  • GithubApiクラス (interfaceを定義)

    import io.reactivex.Observable
    import retrofit2.http.GET
    import retrofit2.http.Path
    
    interface GithubApi {
    
        companion object {
            const val BASE_URL = "https://api.github.com"
        }
    
        @GET("users/{username}")
        fun getUser(@Path("username") user: String): Observable<User>
    }
    
  • MainActivityクラス

    class MainActivity : AppCompatActivity() {
    
        companion object {
            val TAG = MainActivity::class.java.simpleName
        }
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            val retrofit = Retrofit.Builder()
                    .baseUrl(GithubApi.BASE_URL)
                    .addConverterFactory(MoshiConverterFactory.create(Moshi.Builder().build()))
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
                    .build()
    
            val service = retrofit.create(GithubApi::class.java)
            service.getUser("Slowhand0309").subscribe({ ret ->
                Log.d(TAG, "ret $ret")
            }, { error ->
                Log.e(TAG, error.message)
            })
        }
    }
    
AndroidManifestに
<uses-permission android:name="android.permission.INTERNET"/> を追加するのを忘れずに

戻り値に関して

RetrofitのRxJava2 Adapterは戻り値として
Observable, Single, Maybe, Completable
を提供しているが、それぞれの違いがわからないので調べてみる。
  • Observable: いつもの
  • Single
    • onNext+onComplete = onSuccess でonSuccessは一回しか呼べない
  • Maybe
    • onSuccessかonErrorかonCompleteのどれかが呼ばれる、または全く呼ばれない
  • Completable
    • onErrorかonCompleteのどれかが呼ばれる、または全く呼ばれない

エラー処理に関して

200番台以外のレスポンスの場合 HttpException として onError が呼ばれる
実際のソースコード
エラー処理に関しては↓のように捌けば良さそう。
        disposable += ApiClient.createUser()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ res ->
                    Log.d(TAG, "create user success ${res.id}")
                }, { error ->
                    (error as? HttpException)?.let {
                        Log.e(TAG, "code: ${it.code()}")
                        Log.e(TAG, "message: ${it.message()}")
                        Log.e(TAG, "response: ${it.response()}")
                    }
                })
401が返ってきた場合の実際の出力
E/MainActivity: code: 401
    message: 
E/MainActivity: response: Response{protocol=h2, code=401, message=, url=https://....} 

:bomb: バッドノウハウ


単純にmoshiを使うだけだと、dataクラスのnon-nullのプロパティにnullが入ってしまう!
参考URL
そこでmoshiのkotlin extensionを導入して、non-nullにnullを入れうようとしたら
例外を投げるように修正
app/build.gradleに以下を追加
implementation 'com.squareup.moshi:moshi-kotlin:1.5.0'
MainActivityのmoshiを生成している箇所をいかに変更
        val moshi = Moshi.Builder()
                    .add(KotlinJsonAdapterFactory())
                    .build()

        val retrofit = Retrofit.Builder()
                .baseUrl(GithubApi.BASE_URL)
                .addConverterFactory(MoshiConverterFactory.create(moshi))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
                .build()
これでnon-null / null をきっちり管理してくれます:+1:

:link: 関連リンク


0 件のコメント:

コメントを投稿