AsyncTaskでユーザビリティを向上させる
こんにちは。daichi1128です。
前回はHTTP通信をしてTwitterのタイムラインを取得、表示しました。
AndroidでTwitterのタイムラインを取得する【XmlPullParser】 | Techfirm Android Lab
ですが、前回までではユーザのアイコンは動的に表示しておらず、決め打ちで出しているだけでした。それではあまり意味がないので、今回は画像取得部分をやることにします。
ただし、メインスレッド上で画像取得をしてしまうと、取得中はユーザからの操作を全く受け付けない、使い勝手の悪いインターフェースになってしまいます。
そこで、ユーザ操作を受けつけつつ、画像を取得するために、別スレッドで処理を行います。
アプリケーションのユーザビリティを向上させるためには、この非同期処理は必須といえます。今回でマスターしてしまいましょう。
さらに、ついでなので無駄なネットワーク通信をしないために、画像データのキャッシュもしてみます。
というわけで、今回の内容は
- 画像データのキャッシュ
- HTTP通信で画像取得
- AsyncTaskで非同期処理
- その他
です。
画像データのキャッシュ
キャッシュといっても何も大げさなことはしません。
URL文字列(String)をkey、画像データ(Bitmap)をvalueとしたstaticなHashMapを用意して、ネットワーク通信を始める前にこのHashmapを参照して、データがなければHTTPでデータを取得しHashmapにセット、あればHTTP通信をせずにそのvalueを使う、というだけです。
キャッシュデータを管理するImageCacheクラスは以下です。
staticなHashmapフィールドを操作できるメソッドがあるだけのシンプルなクラスです。
# ログを出すためだけに分岐を入れてます。
HTTP通信で画像取得
前回作成したHttpClientクラスは、URL文字列からbyte配列を返すユーティリティメソッドしかないので、Androidで画像データを表すBitmap型へ変換する必要があります。そこで以下のgetImageメソッドをHttpClientクラスに追加して、URL文字列からBitmapを取得できるようにします。
これもシンプルですね。
AsyncTaskで非同期処理
今回のメイン所です。
実はAndroidでは、通常のjavaの感覚でThreadクラスを作って実行すると、エラーで怒られてしまいます。
これはメインスレッドとなるUI Threadが他のThreadからの操作を受け付けない仕組みになっているからです。
この辺のことは、以下のページにとても丁寧に書かれているため、ここでは割愛します。
throw Life – AndroidのHandlerとは何か?
じゃ、どうすればいいのかというと、Androidにはこういった非同期処理を簡単に行えるクラスが既に用意されています。
AsyncTaskクラスです。
使い方にちょっとしたルールがあるので、サンプルコードを交えて説明していきます。
以下はAsynctaskクラスを継承したDownloadTaskクラスです。
バックグラウンド処理の実行
AsyncTaskはインスタンスのexecuteメソッドでバックグラウンド処理を開始します。例えば上記のバックグラウンド処理を実行するには以下のように書きます。
# コンストラクタの引数は重要ではありません。
AsyncTaskの4つのステップ
このようにして、executeを呼ぶと、AsyncTaskの以下のメソッドが順にコールされます。
- onPreExecute()
doInBackground(Params...)onProgressUpdate(Progress...)onPostExecute(Result)
実行前の準備処理をここに書く。例えば、インジケータのセットアップ等。
バックグラウンドで実行したい処理を書く。
バックグラウンド処理の進捗具合をメインスレッドで表示させたい場合にはここに処理を書く。
バックグラウンド処理が終了し、メインスレッドに反映させる処理をここに書く。
今回は簡単のために、doInBackgroundとonPostExecuteを実装しています。
Generics
また、上記メソッドでParams、Progress、Resultといった記述がありますが、これらはGenericsです。
以下のように指定します。
ここで書いているString,Void,Bitmapの3つの型は順にParams、Progress、Resultとして扱われ、それぞれバックグラウンド処理を開始するインスタンスメソッドexecuteの引数の型、進捗度合を表示する時に利用したい型、バックグラウンド処理完了時に受け取る型を意味しています。
今回は非同期処理として、URL文字列(String)から画像データ(Bitmap)を取得したい、進捗度合は特に表示しなくいい、というケースなので、String,Void,Bitmapという順に型を指定しています。
# 型を指定したくない場合はVoidを使います。
コンストラクタでは、バックグラウンド処理の結果を反映させるために、アイコンを表示するImageViewを引数にとっています。
doInBackgroundにはバックグラウンドで実行したい処理を書きます。
この部分で通信をしてBitmapデータを取得しています。
HTTP通信を始める前にImageCacheにデータがあるか確認し、ない場合にのみ通信するようにしてます。
onPostExecuteでは、取得したBitmapをアイコン表示のImageViewにセットしています。ここの処理はメインスレッドに反映されるため、これで画面上に取得したアイコンが表示されます。
AsyncTaskについての詳細は以下を参照してください。
AsyncTask | Android Developers
その他の処理
画像URLフィールドをTwitterStatusに追加
BeanクラスのTwitterStatusに画像URLフィールを追加します。こんな感じです。
元ソースはこちらを参照してください。
ListViewをカスタマイズする | Techfirm Android Lab
パーサで画像URLを取得するようにする
詳細は前回の記事を参考にしてください。
AndroidでTwitterのタイムラインを取得する【XmlPullParser】 | Techfirm Android Lab
twitterからのxmlレスポンスのprofile_image_urlがユーザアイコンのURLになるので、これをパース時に取得するように、以下のように修正します。
コメントがあるのが追加した部分です。
バックグラウンド処理の呼び出し元
Downloadtaskの実行はTwitterAdapterクラスで行うことにします。
TwitterAdapterクラスのgetViewメソッドを以下のように修正。

大変役立つ情報をありがとうございます
かなり参考になり、作りたいアプリが作れそうです
ただ、一点問題があります
リストに表示されるサムネイル(アイコン)の表示が不安定です
リストをスクロールさせると、同じアイコンがなんども繰り返し表示され、しばらく放置すると本来表示されるべきサムネイルが表示される感じです
素早く大量にスクロールすると、本来とは別のサムネイルが表示された状態になってしまう、最悪エラーでアクティビティが終了してしまいます
キャッシュをしないようにしても、症状は改善されませんでした
いきなり質問で大変失礼ではありますが、何か考えられる原因はありますでしょうか?
上記症状は convertView によるものでしたね
変な質問をしてしまい、大変申し訳ありませんでした
自分なりにカスタマイズしたいと思います
[...] ● 参考文献 AsyncTaskでユーザビリティを向上させる AsyncTaskは使い捨て [...]
[...] Android Techfirm Lab [...]