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

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

はじめに

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

RxJavaとRxAndroidとRxLifecycleの関係

RxJavaとRxAndroidはそれぞれReactive ExtensionsのJava向け実装と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は最低でも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
                }
            });
    }
}

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

  • RxAppCompatActivityを継承していること
  • bindToLifecycleメソッドを呼び出していること

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

おわりに

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