先日のDroidconSEで行われた講演で,Matthias Käppler氏(SoundCloudのソフトウェア技術者)は,非同期動作をエレガントに処理するため,AndroidアプリにRxJavaを採用することを提案した。
Android開発者はこれまで,Googleのリードに従って,非同期動作のハンドリングにはAsyncTaskあるいはLoaderパターンを使用してきた。しかしこれらのアプローチには,多くの制限のあることが広く知られている。特に問題なのは,
- Activityの内部クラスとして宣言されたAsyncTaskがActivityへの参照を保持するため,メモリを大量に使用するActivyクラスはしばしばメモリリークを引き起こす。
- デバイスを回転した後にActivityが再生成される場合があるため,それに備えて,AsyncTaskのユーザインターフェース更新処理には防御的なプログラミングを行う必要がある。
- 複数のAsyncTaskあるいはLoaderをオーケストレーションするには,ネスティングを使用するアプローチ以外に手段がなく,扱いの難しいコードになる。
RxJavaは,Java言語によるリアクティブエクステンション実装である。もともとは,ビデオオンデマンドアプリケーションのサービス層において同時実行を処理する目的で,Netflixが開発したライブラリだ。その後2013年2月にオープンソース化されると,先駆的なAndroid開発者たちがすぐに飛びついた。Mattiasはそのような開発者のひとりで,RxJavaを活用するためにSoundCloudのAndroidアプリを設計し,現在ではRxAndroidプロジェクトの重要なコントリビュータになっている。InfoQでは講演を終えた氏に,他の開発者がRxJavaの採用によって得られるメリットについて聞いてみた。
InfoQ: AsyncTaskやLoaderを使い慣れたAndroid開発者が,なぜRxJavaの学習に時間を投資するべきなのでしょう?
Käppler: 私たちがRxJavaを採用したのは,AsyncTaskやLoader,Handler/Messageには,回復性と再利用性を備えたデータ転送手段として不適当な制約があったからです。複合的なAPIのないことが特に問題です。非同期操作を,入力と出力がひとつのステップから次へと引き継がれる一連のステップという,一般的な方法で表現することが難しくなっているのです。これと密接な関係にあるのが,このようなステップをフォールトトレラントな方法で行うというアイデアです。ですが,今挙げたフレームワーククラスはどれも個々のステップのエラーさえ表現できないのですから,シーケンス全体を扱うなど問題外です。データベースやWeb APIコールなど,複数のデータソースの組み合わせが必要になることはよくあります。それにモバイルは,本質的にさまざまな障害(信頼性の低いネットワーク,デバイスのフラグメンテーションなど)の起きやすい環境ですから,AsyncTask,Loader,あるいはHandle/Messageなどは,単独では適切なツールと考えることはできません。RxJavaでは,実行する処理,変換や結果の構成方法を記述する統一的方法を提供することで,この問題に対処すると同時に,シーケンスの成否や障害の位置に関わらず,処理結果ないし障害結果が完結することを保証しています。
InfoQ: RxJavaは,SoundCloudのAndroidアプリケーションのアーキテクチャにどのような影響を与えた,あるいは改善したのでしょう?
MK: 当時の私たちが苦労していた大きな問題のひとつは,Androidがオブジェクト相互の通信手段として,さまざまな方法を提供していることでした。コールバック(名称や使い方に一貫性がないことが多い)やHandler/Message,さまざまな形態のインテント,ContentObserverなどなど,本当に混乱しています。これらのためにRxアダプタを書くことにより,アプリケーション内通信の効率化と,メッセージを転送する統一的な方法の提供が可能になりました。RxJavaが最初から備えている,コンポーネント化やエラー処理などのメリットをフルに活用できたのです。私たちのアプリでもっとも広く使われている型のひとつは,RxのSubjectをベースとしたイベントバスです。他のすべてのオブザーバブル(Observable)が使用しているものと同じRxプロトコルを使った,デカップリング方針のオブジェクト間通信が実現されています。
InfoQ: SoundCloudのモバイルアプリケーションにRxJavaを採用する上で直面した,もっとも大きな課題は何でしたか?
MK: Androidに不可欠なフレームワーククラスの上に関数プログラム方式を適用することの学習曲線が急であること,Rxの実装パターンやイディオムがあまり一般的なものではないこと,ライブラリの状態がプレリリースであること,などでしょうね。最初に導入したRxJavaはバージョン0.5でしたが,1.0の安定リリースが発表されるまで,非互換な修正を数多く行わなくてはならなかったことにも苦労しました。チーム全体が,方針に賛同してくれるようにすることも重要です。私たちの場合,幸運にもこれは大きな問題ではありませんでした。皆が新しいことを試すことや,必要ならば考え方を根本的にシフトすることに積極的だからです。実際のところ,当社のiOSチームはReactiveCocoaを通じて,AndroidチームのRxJavaよりも先にRxを採用していましたから,チーム全体としてRxの思想を受け入れるシフトはすでに存在していたのです。
InfoQ: RxJavaを採用したことによって,テストにはどのような影響があったのでしょう? リアクティブなコードでもユニットテストは可能なのでしょうか?
MK: 簡単に言えば,もちろんです。詳しく言うならば,インターフェースをどのように定義するか,オブザーバブルなシーケンスの公開する場所としない場所はどこか,テストでモックにする部分としない部分はどれかといった点について,初期の学習曲線がありました。私自身,このあたりが理解でき始めたのは,まだ最近のことです。AsyncTaskのような従来的なジョブオブジェクトとしてではなく,バリューストリームとして,オブザーバブルシーケンスがその本質から理解できるようになれば,そして,テスト対象の公開API上で,これらのシーケンスをプラグインしたりプラグアウトしたりできるように,サブスクリプションの結果を直接的あるいは間接的に監視できるようにコードを設計すれば,コードのテスト可能性は飛躍的に高いものになります。もうひとつのメリットは,Rx Schedulerによってシーケンスのスワップインとスワップアウトが可能であるように,並行性がパラメトリックであるため,テスト対象のクラスの動作を変えることなく,ユニットテスト内の式から同時実行を完全に排除することが可能な点です。さらにTestSchedulerを使用すれば,ユニットテストの時間的な流れを操作することもできるのです。これはすごく便利ですよ。
InfoQ: RxJava Androidサブプロジェクト(RxAndroid)の重要なコントリビュータのひとりとして,これから開発者が期待できそうな変更についてコメントをお願いします。特に,UIバインディングに関する開発の予定はありますか?
MK: はい,あります。作業は進行中ですが,多くの開発者が参加して,ライブラリへのコントリビューションを行っています。ビュープロパティをオブザーバブルシーケンスにバインドする,あるいはAndroidフレームワークのクラスをオブザーバブルとして利用できるように既存APIを拡張する,といった作業が中心です。RxAndroid(付属ライブラリを含まないベアボーンとして)に関わるアプリ開発者に対して,より全体的なソリューションを提供し,非同期シーケンスをAndroid Activityライフサイクルにバインドするボイラプレートを不要にするような,既製のフレームワークコンポーネントを利用できるようするという話もあります。ですが,まだ初期の段階ですので,現時点ではあまり詳しい情報を提供することはできません。
InfoQ: 自身のアプリケーションでRxJavaを使いたいと思うAndroid開発者に対しては,どのようなリソースを推奨しますか?
MK: 強くお勧めするのは Lee Cambell[1]の"Introduction to Rx"ですね。Kindle eBookで無償提供されています。このライブラリが最初に考案された理由の紹介や,現実的なユースケースに基づいた基本概念を解説した書籍です。.NETとC#用のオリジナルRxライブラリをベースとしていますが,どのプラットフォームでもアイデアは同じです。Daniel Lew氏はRxJavaに関して,Android専用のブログ記事シリーズを書き始めていますが,これも非常に分かりやすいものになっています[2]。それ以外では,RxAndroidプロジェクト[3]として現在,コードサンプルに関する体裁を整えているところです。先日メインライブラリから分離されたばかりですが,今後に期待してください!最後になりましたが,Erik Meijer氏からRxに関して話を聞く機会があれば,とても素晴らしくて,最高に面白いと思います。鉛筆を持っているエンジニアを信頼できない理由について,教えてくれるでしょう。
氏の書いたリアクティブ宣言(Reactive Manifest)は,リアクティブアーキテクチャの基本原則の概要を説明したものだ。これらの原則は,今年のQCon San Francuscoのリアクティブアーキテクチャのトラックでも取り上げられた。その講演の中では,RxJavaプロジェクトのフェローコントリビュータであるBen Christensen氏が,RxJavaによるリアクティブプログラミングについて説明している。