• スポンサードリンク

MediaStoreから非同期でサムネイルを読み込むAdapterを作ったよ-2

Android 非同期

以前の記事MediaStoreから非同期でサムネイルを読み込むAdapterを作ったよのPart-2だよ。
今回は、サムネイル・イメージの取得と、スレッドプールの生成・使い方の例です。

MediaStoreからのサムネイルの取得

これに気持ち悪いところがあってソースの公開が遅れていたのでした。MediaStore.Imagesからサムネイルを取得するには、MediaStore.Images.Thumbnails#getThumbnailというヘルパーメソッドを使います。このメソッドには、

の2つのバリエーションがあって、BitmapFactory.Optionsを引数に取ります(null可)。で、このBitmapFactory.Optionsの罠に引っかかった訳です。
簡単に言うと、BitmapFactory.Options.inJustDecodeBounds=trueで#getThumbnailを呼び出すと、サムネイルのキャッシュ(ディスクキャッシュ)が無視されて毎回サムネイルの生成処理が走るため、時間がかかってしまうってことでした。

詳しくは、前回の記事MediaStore.Images.Thumbnails#getThumbnailについて調べてみたを見てね。

サムネイルの取得部分はこんな感じです。

サムネイルの大きさは、MICRO_KIND(96×96の四角)と、MINI_KIND(512×384)の2種類有るので、欲しいサムネイルのサイズに合わせて切り替えています。ここはsetter/getterで選択できるようにしてもいいんだけど、判断基準があんまりなさそうなので、幅・高さ・ピクセル数を元に決め打ちしています。

MediaStoreからのイメージの取得

次は、MediaStoreからのイメージの取得です。サクッと行きましょう(^o^)/

実際にイメージの読込みを行っている部分は、こんな感じになります。

定番過ぎて余り書くこともないのですが・・・こちらは、OOM対策で欲しいサイズに合わせてスケーリングしています。これと同じ事をサムネイル取得時にもしようとしていたわけです。
ファイルからの読込みなので、Javaらしく書こうとするとBufferedInputStreamでラッピングしたInputStreamで読込みみたいなことになると思うんだけど、OOM対策で2回読み込もうとすると、ファイルサイズと同じ大きさのバッファを保つ羽目になるので、素直にFileDescriptorを使って読み込んでいます。
ただ、サムネイル取得の、MediaStore.Images.Thumbnails#getThumbnailは、BitmapFactory.Options付きのメソッドしか無いのに、逆に、イメージの取得の方は、BitmapFactory.Options無しのMediaStore.Images.Media#getBitmap(ContentResolver cr, Uri url)しか無いって、逆じゃね?って思いませんか?

ぼやきはこれぐらいにして、このメソッドに、こんな感じに一皮かぶせてあげます。

一瞬にしてAdapter/AdapterView(の子クラス)のposition値からイメージを取得できるようになりました。
getImage(long id, int requestWidth, int requestHeight)とgetImage(int position, int requestWidth, int requestHeight)だと、呼び出しが曖昧になってしまうので、fool proofのために、idで呼び出す方は前のコードのようにprivate staticメソッドにして内部に遮蔽しています。一般的にstaticメソッドの方が呼び出しが速いってのもあるけどね。

スレッドプール

最後はスレッドプールです。AsyncTaskを始めとしてAndroidの内部ではスレッドプールから実行される処理が沢山あります。今回のAdapterの実装では、UIスレッドでの処理をDrawable#scheduleSelf(Runnable what, long when)にお願いしてしまうので、余計な処理の少ないスレッドプールを直接使っています。
スレッドプールの作り方はいくつか有るのですが、今回はThreadPoolExecutorと言うのを使います。では、ソースです。

えっこれだけ?そうこれだけです。しかもうち3行は定数定義なので実質1行です(^_^;)
初期スレッド数4、最大スレッド数32、アイドリング時間10秒、実行待ちの上限無し(Integer.MAX_VALUE=2147483647)で生成しています。また、別の場所で、

としているので、APIレベルが9以上で呼び出しがなければ待機スレッドは0になります。本来であれば、デッドロック・ライブロック対策的に、待機待ちの上限を設けて、破棄や一定時間後にリトライや直接実行とする方が良いのですが、今回は簡略化してます。
ThreadPoolExecutorのコンストラクタは色んなバリエーションがあって、実行待ちの上限を超えた時に破棄するとかそのまま実行するとかなど、色んな種類のスレッドプールを生成できるので、必要があればレファレンスを見てくださいね。

後は、

とすれば、簡単に非同期の処理を行うことが出来ます。
ちなみに、AsyncTaskの場合は、初期スレッド数5、最大スレッド数128、アイドリング時間1秒、実行待ちの上限10(超えた場合には古い物から順に破棄)で生成してるみたいです。つまり実行中が128個で待機中が11個目になった時点で実行されずに闇へ葬り去られてしまうわけですね。そんなに一度に実行することはあんまりないと思いますけど。

でも、非同期処理が終了するのを待ちたいとか、コールバックが欲しいとか、非同期処理の結果を取得したいとかであれば、素直にAysncTaskLoaderとかを使ったほうが簡単かもしれませんね。

ということで今回はおしまいです。
他にも幾つか便利メソッドを実装してあるので詳しくはソースを見てね。お疲れ様でした。

MediaStorePhotoAdapterと簡単なデモアプリはこちらに有ります。GitHub

« »

  • スポンサードリンク

コメント

%d人のブロガーが「いいね」をつけました。