• スポンサードリンク

MediaStore.Images.Thumbnails#getThumbnailについて調べてみた

Android 非同期

先日記事にしたMediaStoreからサムネイルを非同期で読み込むAdapterを作っていて、気になったことがあったので、MediaStore.Images.Thumbnails#getThumbnailについて調べてみました。

結論は2つ。

  1. MediaStore.Images.Thumbnails.getThumbnailを、イメージの大きさを取得するためにBitmapFactory.OptionsのinJustDecodeBoundsをtrueにセットして呼び出すのは推奨できない。サムネイルキャッシュを使用せずに毎回ブロックしながらサムネイルを生成してしまうことになるのでかなり遅くなってしまう。
  2. おまけだけど、少なくともNexus7(2012/Android4.4.2)ではサムネイルキャッシュのディレクトリ自体を削除してはいけない(したら再起動しないとダメ)。中身を消すだけなら可。他のKitKat機はどうなんだろ?力尽きたのでエミュレータでは確認してません

あ〜無駄な時間だった。では迷走の経緯を簡単に・・・

#getExternalStorageDirectoryで取得できるディレクトリをEXTERNALとすると、

  1. EXTERNAL/DCIM/.thumbnailsディレクトリ(以下サムネイルディレクトリ)内にサムネイルのキャシュが存在する。
  2. サムネイルディレクトリ内にキャッシュが存在しない時は、#getThumbnailを呼び出した時にサムネイルディレクトリ内にキャッシュを生成する。この時、Logに”Create the thumbnail in memory…”が出力される。これは予想通りの結果。
  3. ところが、先日記事にしたAdapterをテストしている時に、サムネイルディレクトリ内にキャッシュが存在する場合にもLogに”Create the thumbnail in memory…”が出力されてしまう。これはAndroid2.3.3とかを含めて手持ちの機種全てで発生。何で?MedisStoreクラス内を見てみると、キャッシュが見つからなかった時にこのLogが出力されるみたい。
  4. サムネイルディレクトリを削除してしまうと、手持ちのNexus7(2012,Android4.4.2)では再起動するまでディレクトリを作成できない(root化すれば行けると思うけど)。代わりに、sdcard/DCIM/.thumbnailsファイルが自動的に作成される。何だそりゃ。
    サムネイルディレクトリについては、 で、強制的にSDカードマウント時のブロードキャストメッセージを送りつけると作ってくれるはずなんだけど、手持ちのNexus7では、Permission deniedとなってうまくいかない。KitKatからシステムアプリしかだめになったんだっけ?Android4.1.2とかAndroid2.3.3ではIntent.ACTION_MEDIA_MOUNTEDを送るのは大丈夫だけどそもそもこれらの機種ではサムネイルディレクトリを自動でちゃんと作ってくれる。
  5. 手持ちのNexus7では、サムネイルディレクトリが存在しないと、#getThumbnailが非常に遅くなる(読み込もうとしてエラーになる上に、4.の問題があるから)。他の子達は遅くなるものの、自分でサムネイルディレクトリを作ってキャッシュも生成してくれる。

4と5は色々テストしている中で、キャッシュの無い状態でのチェックをしようとした時に、横着してディレクトリごと消しちゃったからなので、通常は問題ないはず・・・たぶん。でもきっとディレクトリごと消しちゃって超おせ〜とか口走っているのがいっぱいいそうだよなぁ。

それはともかくとして、3です、毎回”Create the thumbnail in memory…”がでる奴です。どう考えてもサムネイルキャッシュを使用してません。
しょうが無いので、まじめにソース上とデバッガでMedisStoreやそこから呼び出される処理まで追いかけた結果、キャッシュが見つからなかった時ではなく、正確には、キャッシュが見つからないかまたはキャッシュからデコードをした結果、サムネイルのビットマップを生成できなかった/しなかった時に”Create the thumbnail in memory…”がログに出力されることが判明しました。これは超心当たりありありです\(^o^)/
昔々組み込みのプログラムの仕事をしていた時にビット単位でメモリをケチる羽目になることが多かったせいか、例えばサムネイルが150px幅しか要らないのに320px幅にするなんて4倍以上メモリ無駄遣いじゃんって思ったんですよね。
それで、レファレンスのMediaStore.Images.Thumbnails#getThumbnailを見るとBitmapFactory.Options付きで呼び出せるのがあったので、じゃぁinJustDecodeBounds=trueで呼び出してビットマップの大きさを取得してサブサンプリングを計算してからinJustDecodeBounds=falseでデコードさせようとした訳なんですよ。

当然の事ながらinJustDecodeBounds=trueだと仮にキャッシュが存在したとしてもビットマップは生成されないので、MediaStore内でデコード失敗として扱われ、その結果、キャッシュは無かったことにされて毎回フルにデコードされる事に・・・しかもがっつりブロックしたまま。レファレンスに書いといて欲しかったです。

ネットで探しても似たような話見つからなかったってことは、今時の端末じゃそこまでメモリをケチらんでもええんかなぁって思うようになってきました。ということでサムネイル画像のサイズ調整はAndroid様にお任せすることに。後はImageViewの設定をゴニョゴニョすればなんとかなるでしょう。

とはいうものの、MediaStore.Images.Thumbnails#getThumbnailを呼び出す時にBitmapFactory.Optionsを付ける時は気をつけましょう(´・ω・`)…

« »

  • スポンサードリンク