Adapterの高速化

By daichi1128
このエントリをはてなブックマークに追加このエントリをdel.icio.usに追加このエントリをLivedoor Clipに追加このエントリをYahoo!ブックマークに追加このエントリをFC2ブックマークに追加このエントリをNifty Clipに追加このエントリをPOOKMARK. Airlinesに追加このエントリをBuzzurl(バザール)に追加このエントリをChoixに追加このエントリをnewsingに追加

Xperia発売されましたね。
うちのPICTRHYTHMもよろしくね!
http://www.techfirm.co.jp/pictrhythm/

と、宣伝もそこそこに今回は、Adapterの高速化にトライします。

Adapter

Androidの中でAdapterクラスは頻繁に使われます。
Adapterクラスは、データソースとビューのつなぎ役を果たすものです。

データの一覧を表示するには、ListViewやGridView、Galleryなど様々なViewがありますが、これらのViewにはsetAdapterメソッドがあり、adapterがセットされると、表示すべきデータはこのアダプターから取得するようになります。

Adapterが内部データの違い(データがArrayだったり、Listだったり、Cursorだったりなど)を吸収してくれるので、Viewは余計なことを考えずに描画に集中できるわけです。

Android LabでもListViewをカスタマイズする | Techfirm Android LabでArrayAdapterを使っています。

Adapterはデータソースとビューのつなぎ役なので、表示すべきビューをデータソースから組み立てるのが仕事です。何番目のデータをどのようなView構造で表示するか、それを決定するのがgetViewメソッドです。

このgetViewメソッドは、新しいデータが表示されるタイミングで呼び出されます。ListViewなら、スクロールして、画面外から新しいデータが表示されるタイミングです。こういった性質上、スクロールの度に何度も何度も呼び出されるため、getViewメソッドで無駄な処理があると、スクロールにひっかかりが出来るなど、パフォーマンスに直結した悪影響を及ぼします。

Viewの再利用

まず、getViewで確認すべきところはしっかりViewを再利用しているかです。
getViewはこんなシグニチャになってます。

positionは何要素目か、contentViewはposition番目のビューを、parentは親のビューグループです。

AsyncTaskでユーザビリティを向上させる | Techfirm Android Lab

で書いていたコードが以下です。

改めて見てみると、パフォーマンス以外にも無駄な処理がありますが、今はあまり気にしないでください。

ひとまずはViewの再利用を確認しましょう。

上のコードでは、引数で受け取ったconvertViewをviewに受けて、nullチェックをしています。
nullならviewを生成し、nullでないならそれを使う、という流れになっています。
最後にviewをreturnで返しています。

これにより、次回からposition番目のcontentViewは、以前returnしたviewが渡されるようになります。
見事再利用されています。
viewのnullチェックをせずに毎回inflateやnewする作りでは無駄が多いので、必ず再利用するようにしましょう。

子ビューへのアクセスを高速化

まだ高速化の余地があります。
それは

などとして、毎回ビューをリソースから引っ張りだしている所です。Activityではこういったビュー要素はインスタンス変数へ格納して、アクセスのショートカットをしますが、ここではgridViewなどのセルに対してなので工夫が必要です。

ここでは、ViewHolderというセル内の要素への参照を持つだけのクラスを用意することにします。
こんなクラスです。

このビューホルダーを使って、改善したコードが以下です。

ビューの再利用のために、nullチェックをするところは同じです。
違うのは、ビューを初めて作るタイミングでViewHolderをnewして、holderに子ビューであるTextViewやImageViewの参照をセットし、それをview.setTag()でタグとして登録している所です。

これにより、再利用可能なビューが渡された時、view.getTag()として、子ビューへの参照を持ったViewHolderを取得できるようになります。

findViewByIdでviewを探す処理が省けるのでその分パフォーマンス改善が見込めます。

# DownloadTask内でキャッシュ判定したいたのを、こっちでやるように変更してます。

以上です。

もちろん、これもこちらを参考にしています。

関連する投稿


  1. [...] ● 参考文献 Adapterの高速化 ListViewをカスタマイズする AsyncTaskでユーザビリティを向上させる AsyncTaskは使い捨て [...]

  2. [...] Adapterの高速化 カテゴリー: Google Web Toolkit   パーマリンク ← ProFTPDのFTPS対応(FTP over SSL/TLS) [...]

  3. はじめまして、スィンと申します。

    android の開発記事を大変参考にさせていただいております。
    こちらの記事にあるように、Viewの再利用をしてみました。

    すると、1行目のRowの画像がパタパタと色々変わってしまうのです。
    なにか組み方が悪いのでしょうか?

    もし、この症状の原因と対処法をご存知でしたら、ご教授いただけますと幸いです。
    以上、よろしくお願い致します。

    あと、上記ソースでは、画像がキャッシュにある場合、
    imageView に画像をセットしていないのではないでしょうか?

  4. なかむら より:

    >スインさん

    横から失礼します。なかむらと申します。
    私も同じ現象に陥りました。そのときの解決法が役に立てればご紹介します。

    そもそも原因ですが、AdapterがViewを使いまわしてるため、非同期で画像が読み込み終わったとき、
    持たせたImageViewにsetしても、そのViewは本来とは違うところで使用されている。ということらしいです。

    そのため私が行った方法は、非同期で取得終わったときにViewにsetするのではなく、画像キャッシュに入れてから、notifyDataSetChanged()を呼ぶようにしました。

    また、キャッシュに画像が無い場合、非同期処理を開始するとともに、ImageViewにはnullをセットしておきました。そうすることで未取得の画像のところに他の画像が表示されることがなくなります。

  5. [...] Adapterの高速化 | Android Techfirm Lab [...]

Leave a Reply

Additional comments powered by BackType