Androidアプリの非同期処理ライブラリをRxJava3にバージョンアップしました

N予備校Androidアプリでは、tatsuyafujisakiさん、naka2ttsuさんにご協力いただき、非同期処理を行うライブラリをRxJava3にバージョンアップしました。

これまではRxJava2を使用していましたが、RxJava2のサポートが2021年2月末で終了してしまうため、それに合わせて更新しました。

The 2.x version is in maintenance mode and will be supported only through bugfixes until February 28, 2021. No new features, behavior changes or documentation adjustments will be accepted or applied to 2.x.

github.com

この記事では、RxJava2からRxJava3にバージョンを上げるときの注意点、実際の手順と、移行して得られたことについてまとめます。

RxJava3へバージョンアップするときの注意点

RxJava2からRxJava3に移行するためには、全てのコードを書き換える必要があります。なぜなら、RxJava2とRxJava3に互換性が全くないからです。

そもそもパッケージ名が異なるので、全く別のライブラリとして扱う必要があります。

RxJava2: import io.reactivex.Observable
RxJava3: import io.reactivex.rxjava3.core.Observable

RxJavaは、新たな機能を自由に導入したり、過去の技術的負債を捨てるために、メジャーバージョン間の互換性を持たせない方針を取っています。

これはつまり、メジャーバージョンを更新するたびに開発者は全てのコードを置き換えなければいけないわけで、その点ではKotlin Coroutine, Flowのほうが非同期処理のライブラリとしてはメンテナンスがしやすいです。

アプリがシンプルな非同期処理だけを行うのであればKotlin Coroutine, Flowへの置き換えを行っても良いでしょう。しかしN予備校アプリでは、生放送の授業や、動画で学習する教材などにおける複雑な処理にRxJavaを使用しており、それらの処理を現状のKotlin Coroutine, Flowで置き換えるのは不安がありました。

そのため、今回はRxJava3へのバージョンアップを行いました。今後のKotlin Flowの発展によっては、非同期処理ライブラリを変更するかもしれません。

RxJava3へのバージョンアップ手順

RxJava3に移行するにあたっては、RxJava2とRxJava3の互換性を生み出してくれるRxJavaBridgeを使用しました。

github.com

ReactiveXのメンバーでもあるakarnokd氏が作成しており、RxJava3のドキュメントにも掲載されていることから、こちらを採用しています。

N予備校のAndroidアプリでは、MVVMを拡張した以下のアーキテクチャになっているため、まずはビジネスロジックを持つUseCaseからRxJava3に移行しました。

f:id:hiraike32:20210305191539p:plain
Androidアプリアーキテクチャ図

そして全てのUseCaseをRxJava3に置き換えたところで、UseCaseを発火させるPresenterの処理、UseCaseが呼び出すRepositoryの処理をRxJava3に置き換えます。

このように各レイヤーごとに分割してRxJava3移行を行うことで、網羅的に置き換えられ、かつ複数人で並列で作業を進めることができました。1つ1つの修正も最小限に留められるので、新機能実装などの合間にサクサク行えたことも大きいです。

結果として、RxJava3移行にかかった時間は約4ヶ月、作成されたPull Requestの数は1,081件となりました。これでRxJava2からの脱却が完了しました。

RxJava3に移行したことで得られたこと

RxJava3に移行したことで得られたことはいくつかあります。

古くなっていたロジックの整理ができたこと

UseCaseをRxJava3に置き換える中で、すでに不要になっているロジックの削除や、書き方が冗長になっている部分の修正を行うことができました。

N予備校Androidアプリは2016年4月にリリースされており、当時の技術では難しかった実装が、現在の技術によって簡潔に実装できるケースも多かったです(特に画面遷移やバックキーの制御など)。

ライブラリの置き換えでコードを修正するのは手間はかかったものの、結果として全てのロジックの整理ができ、開発速度の向上やサービスの安定性の向上につながりました。

不要なRx依存から脱却できたこと

今までN予備校Androidアプリでは、画面のボタンがタップ、ロングタップされたら値をObserverに通知するためにRxBindingに、Fragment, Activityのライフサイクルを監視するためにRxLifecycleに大きく依存していました。

しかし、これらのライブラリもRxJava3との互換性を持っていないので、それぞれRxJava3とだけ互換性がある新しいバージョンに置き換える必要があります。

今後RxJavaがバージョンアップするたびに、これらのライブラリも全て修正しなければならないのは辛いので、RxJava3へのバージョンアップと同時に依存の脱却を行いました。

// RxBinding
val submitButton: Button
  get() = findViewById("R.id.button")
// ボタンがタップされたらObservableを流す
val submitButtonObservable: Observable<Unit>
  get() = RxView(submitButton).clicks(pictureButton)
        .subscribeOn(AndroidSchedulers.mainThread())
        .map{ }

// RxBindingから脱却後
val submitButton: Button
  get() = findViewById("R.id.button")
val submitButtonSubject = PublishSubject.create<Unit>()
val submitButtonObservable: Observable<Unit>
  get = submitButtonSubject.hide().share()
// ボタンがタップされたらObservableを流す
submitButton.setOnClickListener {
  submitButtonSubject.onNext(Unit)
}

このようにして、RxJava3への移行が完了して、全体のコードも改善することができました。

今後もAndroidチームでは積極的にライブラリの入れ替えや、技術的負債の解消、そして新機能の追加を行っていきます。

Androidチームとしてやっていきたいこと、N予備校全体としてやっていきたいことはこちらの採用資料に記載していますので、よろしければご覧ください。

speakerdeck.com

Androidチームの採用活動も継続中ですので、ぜひお気軽にお声がけください。

dwango.co.jp