2012年6月14日木曜日

AndroidのListViewを速くするためにやったこと

先日公開した、電話帳アプリ「OneHand Dialer」ですが、品質、性能面で問題ありとのご指摘を受けました。
今回は、備忘録も兼ねて、対策内容をまとめておきます。


Android ListViewを速くするためにやったこと
  1. 現象と原因
  2. 正規表現の使用は極力避ける
  3. Bitmapはキャッシュする
  4. BitmapはBitmapFactory.Optionsを使って縮小する
  5. おまけ:GC発生箇所の調査環境構築


  1. 現象と原因

以下の現象が発生するとのことで、調査を行いました。

・端末:GALAXYNexus(Android 4.0)
・起動時の読み込みに5秒ほどかかる
・データ件数は300〜400件程度
・その他、よく落ちるとのこと(詳細不明)

うーん…。

自分の端末での動作確認のときは、そんなに連絡先の件数が多くなかったこともあり、再現できてなかったのですが、 同等件数のデータと顔写真データが設定されている端末での動作確認を行ったところ起動時に時間がかかる現象は発生させることができました。

よく落ちる現象は手元の端末では再現させることができなかったのですが、別途Android4.0の環境で動作確認を行ってみたいと思います。

原因は、連絡先一覧の取得〜表示処理までに大量のGCが発生していたためでした。
以下、個別の原因と対策です。


  2. 正規表現の使用は極力避ける

OneHand Dialerでは、あいまいソートという機能により、ふりがなに、ひらがな/全角カタカナ/半角カタカナが混在していても、同一視して並べ替える機能があります。
実装には、Comparatorインターフェースを実装して独自の比較を定義しています。

その中で、ひらがな/全角カタカナ/半角カタカナなどを区別するために正規表現を利用していたのですが、そこで、短命オブジェクトが大量に生成されており、GCの対象となっていました。

件数が多くなるほどcompareメソッドが呼ばれる回数が多くなるため、GC発生回数が増えたというわけです。

今回は、正規表現による判定をUnicodeBlockで代用することで回避できました。


また、大文字、小文字の統一も当初、正規表現で判定して、個別にtoUpperCaseメソッドを行っていたのですが、これも短命オブジェクトを大量に生成していたため、最終的な比較処理で利用していたcompareToメソッドをcompareToIgnoreCaseに変更することで条件を除外する対策も行いました。

今回は、代用する条件がありましたが、正規表現でしか表現できない条件を繰り返し利用する場合は、性能面で注意が必要だと思います。
何か対策をご存知のかたがいらっしゃれば、教えていただければと思います。


  3. Bitmapはキャッシュする

OneHand Dialerでは、連絡帳一覧に顔写真を表示しています。
また、ImageViewの角を丸く見せるため、Bitmap生成処理が多少複雑になっており、これらが、ListViewのgetViewの度に呼ばれることで、メモリを圧迫していました。

対策として、Bitmapの一覧をキャッシュするよう変更しました。
また、キャッシュにはソフトリファレンスを利用することで、キャッシュによるメモリ圧迫を回避しています。

実装に関しては、以下が参考になると思います。
Bitmapのキャッシュ
Techfirm Android Lab / AsyncTaskでユーザビリティを向上させる
ソフトリファレンスによるキャッシュ
Techfirm Android Lab / CacheオブジェクトにはSoftReferenceを


  4. BitmapはBitmapFactory.Optionsを使って縮小する

大きなサイズのBitmap生成はコストが高い処理です。
リサイズもできますが、元のサイズのデータを一旦メモリに展開して行うため、非効率的です。

今回は、BitmapFactory.Optionsを使った効率的なBitmapの生成を行うことで対応しました。

AndroidのBitmapFactory.Optionsを使うと、メモリ展開前に画像サイズを取得できるため、最初からリサイズした状態で(少ないメモリで)Bitmapを生成することが可能です。

ポイントは、inSampleSizeプロパティです。
リサイズの縮尺を整数で指定します。1で等倍、2で1/2のサイズにリサイズされます。
ここでは、viewのサイズに合わせたサイズになるように値を求めています。


BitmapFactory.Optionsの使い方は以下を参考にしました。
効率的なBitmapの生成
iPhone充日記 / ListViewに画像を非同期で読み込む場合の注意


  5. おまけ:GC発生箇所の調査環境構築

GCの発生箇所の特定は以下を参考にしました。

MAT環境の構築
電脳羊(Android Dream)/ Eclipseで使うMemory Analyzerのインストール
※自分の環境では、Eclipseとの連動のため、ここにある手順に加えて、Eclipse -> 環境設定 -> Android -> DDMS -> HPROFオプションで「Open in Eclipse」を選択する必要がありました。

MATによるヒープタンプの見方
Bescottee / メモリリークを発見!Androidアプリのメモリ解析手法

※上記のMATに加え、DDMSタブのAllocation Trackerも参考にしました。

以上です、どなたか参考になれば幸いです。ではでは。


0 件のコメント:

コメントを投稿