RxJavaを使ったAndroidアプリにおける非同期処理

はじめに

RxJava Advent Calendar 2015の2日目の記事です。

最近何かと話題なReactive ExtensionsのJVM向け実装であるRxJava(+RxAndroid)を使って、Androidアプリの非同期処理を実装する方法を紹介します。RxJavaとRxAndroidだけでAndroidの非同期処理を実装可能ですが、RxLifecycleというライブラリも同時に使うことで何かと面倒なActivity/Fragmentのライフサイクルのハンドリングを簡単に行うことが可能になります。今回はRxJava、RxAndroid、RxLifecycleの3つを使う方法についてまとめました。

RxJavaとRxAndroidとRxLifecycleの関係

RxJavaとRxAndroidはそれぞれReactive ExtensionsのJVM実装、Android向け実装です。RxLifecycleはAndroidアプリ開発においてライフサイクルを持ったオブジェクト(ActivityやFragment)を管理するためのものです。RxLifecycleを使わなくてもこの記事と同じことが実現出来ますが、ライフサイクルの管理を自前で行わなければならないため、特に理由がない場合は一緒に導入することをお勧めします。

それぞれのリポジトリはこちらです。

AsyncTaskを使った非同期処理

まず、はじめにAndroid標準のAsyncTaskを使った非同期処理を見てみます。

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);

        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                SystemClock.sleep(20000);
                return null;
            }
        }.execute();
    }
}

このコードはかなり極端な例ですが、上記のコードを実行して画面遷移や画面回転等のActivity破棄/再生成を伴う操作をするとMainActivityがリークするという問題を抱えています。なぜかと言うと、Javaの匿名クラス(ここではAsyncTask)はアウタークラス(ここではMainActivity)への暗黙の参照を持っているからで、上記の例ではAsyncTaskは最低でも20秒は裏で動き続けるため、AsyncTaskから参照されているMainActivityがGCで回収されずにリークが発生してしまいます。

RxJavaを使った非同期処理

public class MainActivity extends RxAppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Album.getAllAlbumsObservable().compose(this.<List<Album>>bindToLifecycle())
            .subscribe(new Action1<List<Album>>() {
                @Override
                public void call(List<Album> albums) {
                    // Do something
                }
            });
    }
}

Album.getAllAlbumsObservable()はRxJavaにおけるObservableを返すと考えてください。

今回のポイントは以下の2点です。

  • RxAppCompatActivity を継承していること
  • compose(this.<List<Album>>bindToLifecycle()) を呼び出していること

まず、RxAppCompatActivityについてですが、これはRxLifecycleが提供しているクラスで、ライフサイクルを管理するための実装が含まれています。次に、RxAppCompatActivityのbindToLifecycle()を呼び出すことでライフサイクルを自動で管理してくれます。RxLifecycleを使わない場合はsubscribeしたObservableをどこかのタイミングで明示的にunsubscribeする必要がありますが、上記のように実装することで明示的なunsubscribeが必要なくなります。つまり、RxLifecycleを使えばunsubscribeをしてないことによるActivity等のリークを防ぐことが可能になります。

おわりに

非同期処理のためにRxJavaとRxAndroidを使うのは本来の使い方からは少しズレていますが、標準のAsyncTaskが色々と問題があることを考えるとこの使い方もアリなのかなと思います。