Android 開発における通信
アプリ開発において不可欠とも言える Web 通信ですが、 Android だと少し面倒だと思ったことはありませんか。
最近の Android では、通信はメインスレッドでは行ってはいけません。つまり、別スレッドで非同期処理をする必要があります。
しかし、 UI の変更はメインスレッドでしかできないという仕様です。
ここでつまずいてコードが汚くなった経験、皆さんにはないでしょうか、僕にはあります。
非同期通信処理を簡単に行いたい
そこで今回は ApiCaller という抽象クラスを実装しました。
まずはこのApiCaller を用いると通信処理をどれだけ簡単に書けるか見ていただきましょう。
(もったいぶった説明は不要だという上級者の方はこちら ApiCaller )
今回は通信ができていることがわかれば十分なので、 HatenaBookmarks から私の最近のブックマークを API から取得し、受け取った XML をそのまま画面に表示します。
このようになります。
レイアウトは、ScrollView の中に TextView があるシンプルな構成です。
TextView には @+id/text で ID を付与しています。
MainActivity.java は以下のようになっています。
gist4e1df9ef42de2a0a76812781aca841dc
setContentView() で レイアウトを貼り付け、その後、 BookmarkApiCaller を使って通信を行っています。
BookmarkApiCaller のインスタンスを生成する際に @+id/text で ID を付与したテキストビューを渡しています。
BookmarkApiCaller のソースコードは以下のようになっています。
gistbb8b63515e9de6b57c703271b67cdbf9
通信の処理が簡潔に書かれていることがわかります。
callApi(String url) を呼び出して通信を行い、UI の変更を onResponseReceived(String responseBody) に書いているだけです。
BookmarkApiCaller は ApiCaller を継承しています。
ApiCaller を継承すると、通信を行う callApi(String url) メソッドが使用できるようになり、 callApi(String url) に URL を渡すだけで、通信を行うことができます。
通信が完了した際の処理は onResponseReceived(String responseBody) を Override することで記述できます。ここでは TextView に responseBody をセットしています。
通信に失敗した場合は responseBody 呼び出されないので注意してください。
さて、ここまでで通信を簡単に行えることが確認できました。
その立役者である ApiCaller のコードは以下のようになっています。
この ApiCaller を構成する 3つの要素は、mHandler メンバと、callApi() メソッドと、onResponseReceived() メソッドです。
mHandler
mHandler は Handler クラスのメンバで、コンストラクタでインスタンスが生成されます。
このコンストラクタをメインスレッドから呼ぶことで、この mHandler を使ってメインスレッドに干渉できるようになります。
callApi(String url)
実装を持つメソッドで、 ApiCaller を継承したクラスから使えるようになります。
引数の url にアクセスし、そのリソースの取得を非同期に行います。
リソースの取得に成功した場合のみ、mHandler を用いて onResponseReceived() を呼び出します。
mHandler をメインスレッドと紐付けておけば、onResponseReceived() で UI の変更も行うことができます。
onResponseReceived(String responseBody)
リソースの取得が成功した時に実行されるメソッド。
実装を持たないので、ApiCaller を継承したクラスがこのメソッドの実装を記述する必要がある。
裏を返せば、 onResponseReceived() さえ実装すれば、非同期通信処理のことは深く考える必要がない。
まとめ
Android 開発において、非同期通信処理を簡単に書けるようにする ApiCaller を実装しました。
ApiCaller は抽象クラスで、これを継承したクラスで callApi(String url) を呼び出し、 onResponseReceived(String responseBody) を実装するだけで通信が行えます。
また、ApiCaller のコンストラクタをメインスレッドで実行させれば、onResponseReceived(String responseBody) で UI の変更も可能です。
余談
今回実装した ApiCaller は実装を持つ callApi() から、実装を持たない onResponseReceived(String responseBody) を呼び出しています。
これはデザインパターンでいう、TemplateMethod と呼べる実装なのかなと思いました。