私のルール
パフォーマンスの問題が発生するたびに、次のルールに従います。
- 常に測定する -あなたの目で最適化することは悪い考えです。 同じアニメーションを何度か見たとき、より速く動くように思われます。 数字は嘘をつかない。 私が説明するツールを使用し、変更を加える前後にアプリケーションの動作を数回測定します。
- 低速デバイスを使用する -すべての弱点を見つけたい場合は、低速デバイスが役立ちます。 新しい生産的なデバイスでは、潜在的なパフォーマンスの問題は表示されず、すべてのユーザーが最高のデバイスを持っているわけではありません。
- トレードオフ —パフォーマンスの最適化はすべてトレードオフです。 あなたは別のものと引き換えに一つを最適化します。 多くの場合、この他のトレードオフはパフォーマンスの検出と修正に費やされる時間であり、ビットマップの品質または特定のデータ構造に格納する必要があるデータの量でもあります。 何かを犠牲にする準備をしてください。
システムトレース(Systrace)
システムトレースは、使用しない可能性のある最高のツールの1つです。 開発者は、提供するデータをどう処理するかわからないためです。
システムトレースは、電話で何が起こっているかの概要を示します。 このツールは、手に持っている携帯電話が同時に多くのことを実行できる強力なコンピューターであることを思い出させてくれます。 SDKツールの最近の更新では、このツールはデータからツールチップを生成することで改善され、問題の発見に役立ちました。 トレースファイルがどのように見えるか見てみましょう。

Androidデバイスモニターツールを使用してトレースファイルを生成できます。 android Studio Tools> Android> Android Device Monitorの場合Eclipse Window> Perspectives> DDMSの場合、パネルのSystraceボタンをクリックします。 オプションを設定し、[OK]をクリックします。 詳細は
こちら 。
アラートとフレームは興味深いもので、収集されたデータから生成されたプロンプトを示します。 トレースを見て、上記の警告のいずれかを選択してみましょう。

警告は、View#draw()への長い呼び出しがあったことを示しています。 関連する問題に関する説明、ドキュメントへのリンク、さらにはビデオリンクも提供されます。
[フレーム]列を見ると、レンダリングされた各フレームの指定の下に、このフレームのレンダリング中のパフォーマンスの問題を示す緑、黄、赤の色が付いています。 赤いフレームの1つを選択しましょう。

以下に、このフレームに関するすべての警告を示します。 それらのうち3つがあり、そのうちの1つは先ほど見ました。 このフレームを拡大して、以下の警告を明らかにしましょう。ListViewリサイクル中の充填(「ListViewリサイクル中のインフレーション」)。

この部分は32ミリ秒であり、これは16ミリ秒を超えていることがわかります-60フレーム/秒に到達するための要件。 このフレームには、ListViewの各アイテムの詳細な時間情報があります。5つのアイテムのそれぞれに約6ミリ秒が費やされています。 説明は、問題を理解するのに役立ち、解決策を提供します。 上部のグラフでは、ビジュアライゼーション全体が表示され、レイアウトを埋める部分を増やしてビューを表示することもできますが、レイアウトを埋めるのに時間がかかりました。
ゆっくりとレンダリングするフレームの別の例:

フレームを選択したら、mキーを押して強調表示し、この部分にかかった時間を確認します。 見上げると、このフレームのレンダリングに19ミリ秒かかったことがわかります。 警告を拡大すると、「スケジュールの遅延」(「スケジューリングの遅延」)が発生したことがわかります。
スケジュール遅延とは、この部分を処理するスレッドが長時間プロセッサーをキューに入れなかったことを意味します。 そのため、このスレッドを完了するのに時間がかかりました。 このフレームで長いセクションを選択すると、より具体的な情報が表示されます。
壁の長さ(壁の長さ)は、フレームのこの部分の開始から終了までに経過した時間です。 ストリームの開始時に壁時計を見るようなものだから、そう呼ばれます。
CPU時間は、プロセッサがこの部分の処理に費やした時間です。
ウォール時間とプロセッサー時間には大きな違いがあります。 最初は18ミリ秒かかり、次は4ミリ秒かかりました。 これは少し奇妙なことなので、今がプロセッサがこれまで何をしていたかを見るのにふさわしい時です。

4つのコアはすべて十分にビジーでした。 ストリームの1つを選択すると、理由はcom.udinic.keepbusyappであることがわかります。 この場合、別のアプリケーションがプロセッサーをロードし、アプリケーションへの時間の割り当てを拒否しました。 通常、このシナリオは一時的なものです。バックグラウンドの他のアプリケーションはプロセッサを自分自身に引き継がないことが多いため、これらのスレッドはアプリケーション内の別のプロセス、またはメインスレッドである可能性があります。 システムトレース(Systrace)調査ツールには、どこまで行けるかという制限があります。 プロセッサがアプリケーションに読み込まれている理由を見つけるために、別のTraceviewツールを使用します。
Traceviewを表示
各メソッドの実行時間を示すトレース分析ツールを表示します。 トレースファイルがどのように見えるか見てみましょう。

このツールは、Androidデバイスモニターまたはコードから起動できます。 詳細はこちら。
developer.android.com/tools/debugging/debugging-tracing.htmlさまざまな列を見てみましょう。
名前 -グラフ上のメソッドの名前と色。
包括的CPU時間 -プロセッサがこのメソッドとその子(呼び出すすべてのメソッド)を処理する時間
Exclusive CPU Time-プロセッサがこのメソッドを処理する時間。内部で呼び出されるメソッドは除外されます。
包括的/排他的リアルタイム -メソッドの開始から終了までの時間。 システムトレース(Systrace)のウォールタイムと同じです。
呼び出し+再帰 -メソッドが呼び出された回数と再帰呼び出しの数。
プロセッサ/コールごとのリアルタイム(CPU /コールごとのリアルタイム) -平均CPU /コールごとのリアルタイム。 他のフィールドには、すべてのメソッド呼び出しの合計時間が経時的に表示されます。
スクロールがスムーズに機能しないアプリケーションを長時間開いた。 トレースを開始し、少しスクロールしてトレースを停止しました。 getView()メソッドを見つけて拡張しました。見たものは次のとおりです。

このメソッドは12回呼び出され、プロセッサは各呼び出しで約3ミリ秒を費やしましたが、各呼び出しを完了するためのリアルタイムは162ミリ秒でした! もちろんこれは問題です...
このメソッドの子を見ると、異なるメソッド間の時間の分布がわかります。 Thread.join()は、包括時間の98%を要しました。 別のスレッドの完了を待つ必要がある場合に、このメソッドを使用します。 他の子の1つはThread.start()です。これは、getView()メソッドがスレッドを開始し、スレッドの終了を待機することを示唆しています。
しかし、このストリームはどこにありますか?
getView()はこの作業を直接行わないため、このスレッドがgetView()の子として何をしていたかはわかりません。 これを見つけるために、新しいスレッドの開始時に呼び出されるThread.run()メソッドを探していました。 犯人を見つけるまで続けました。

BgService.doWork()メソッドの呼び出しに約14ミリ秒かかったことがわかりました。 各getView()がBgService.doWork()を1回以上呼び出す可能性があり、getView()への各呼び出しに時間がかかる理由を説明します。 このメソッドは、長時間プロセッサーをロードします。 プロセッサの例外時間を見ると、トレースの合計時間の80%を使用していることがわかります。 例外的なプロセッサー時間でソートすることも、トレース内のロード済みメソッドを見つけるための良い方法です。 これらはパフォーマンスの問題に関係している可能性があります。
getView()、View#onDraw()などの次の重要なメソッドは、アプリケーションが遅い理由を見つけるのに役立ちます。 しかし、時には、何か他のものがプロセッサをロードし、インターフェイスのよりスムーズなレンダリングに費やすことができる貴重なプロセッササイクルを使用します。 ガベージコレクターはランダムに実行され、未使用のオブジェクトをクリーンアップします。通常、フォアグラウンドで実行されているアプリケーションには影響しません。 ガベージコレクターがあまりにも頻繁に起動する場合、これはアプリケーションにブレーキをかける可能性があり、おそらくこれが私たちの障害です。
メモリプロファイリング
Android Studioは、パフォーマンスの問題を見つけるのに役立つ多数のツールで最近大幅に改善されました。 Androidウィンドウの[メモリ]タブには、ヒープに割り当てられたメモリの量が経時的に表示されます。 これは次のようなものです。

グラフに小さなドロップが表示されると、ガベージコレクションが発生し、使用されていないオブジェクトが削除され、ヒープ上のスペースが解放されます。
グラフの左側には、2つのヒープダンプツールとAllocation Trackerがあります。
ヒープダンプ
現在ヒープ内にあるものを考慮するために、ヒープダンプボタンを使用できます。 その結果、現在ヒープ上にあるもののスナップショットが取得され、Android Studioの特別なレポート画面にこれが表示されます。

左側には、ヒープ上のインスタンスのヒストグラムがクラス名ごとにグループ化されて表示されます。 インスタンスごとに、メモリ内のオブジェクトの数、これらのインスタンスのサイズ(浅いサイズ)、およびメモリに格納されたこれらのオブジェクトのサイズがあります。 後者は、これらのインスタンスが解放された場合に解放できるメモリ量を示します。 このビューは、アプリケーションのメモリ容量を示し、大規模なデータ構造とオブジェクトの関係を識別するのに役立ちます。 この情報は、オブジェクトの関係を解放して割り当てられたメモリを削減し、最終的にメモリを可能な限り削減することで、より効率的なデータ構造を作成するのに役立ちます。
ヒストグラムを見ると、MemoryActivityには39個のインスタンスがあることがわかります。これはアクティビティにとって奇妙です。 右側のインスタンスの1つを選択すると、以下のリンクツリーにこのインスタンスへのすべてのリンクが表示されます。

それらの1つは、ListenersManagerオブジェクトの配列の一部です。 アクティビティの他のインスタンスを見ると、それらはすべてこのオブジェクトによって保持されていることがわかります。 これは、このクラスの唯一のオブジェクトが非常に多くのメモリを占有する理由を説明しています。

このような状況は一般にメモリリークと呼ばれます。アクティビティがクリーンアップされ、この未使用のメモリがこのリンクのためにガベージコレクタによって削除されないためです。 長期間存在する他のオブジェクトがオブジェクトによって参照されないようにすることで、このような状況を回避できます。 この状況では、アクティビティが破棄された後、ListenersManagerはこのリンクを保存しないでください。 ソリューションは、onDestory()コールバックメソッドでアクティビティが破棄される前にこのリンクを削除します。
メモリリークやその他の大きなオブジェクトはヒープ上の多くのスペースを占有し、使用可能なメモリを減らします。その結果、ガベージコレクタはメモリを解放しようとする多くのイベントを発生させます。 これらのガベージコレクタイベントはプロセッサを占有し、アプリケーションのパフォーマンスが低下します。 使用可能なメモリの量がアプリケーションにとって十分ではなく、ヒープがそれ以上成長できない場合、OutOfMemoryExceptionが発生し、アプリケーションがクラッシュします。
より高度なEclipse Memory Analyzer Tool(Eclipse MAT):

このツールはAndroid Studioと同じように実行でき、潜在的なメモリリークを特定し、2MBを超えるすべてのラスターイメージやすべての
空のRectオブジェクトの検索など、インスタンスの検索を改善し
ます 。
もう1つの優れたツールは、オブジェクトにリークがないことを保証する
LeakCanaryライブラリです。 何がどこで起こったかを思い出させてくれます。

割り当てトラッカー
メモリグラフの左側にある他のボタンを使用して、メモリ割り当てトラッカーを開始/停止します。 この期間中にメモリ内にあるすべてのインスタンスのレポートをクラスごとにグループ化して生成します。

またはメソッドによって:

メモリ内の最大インスタンスを示す優れた視覚化もあります。 この情報を使用して、メモリが過剰に割り当てられ、多くのガベージコレクタイベントをトリガーできる、タイムクリティカルなメソッドを見つけることができます。 また、寿命の短い同じクラスの多くのインスタンスを見つけることができます。この場合
、オブジェクトの
キューを使用して、メモリ割り当ての総数を減らすこと
ができます 。
一般的なメモリのヒント
コードを書くときに使用する簡単なヒント/チュートリアルを次に示します。
列挙型は 、パフォーマンスに関する議論のホットなトピックです。 リスティングが占めるサイズを示すこれ
に関するビデオと
、このビデオの
議論と誤解を招くような情報があります。 列挙は通常の定数よりも多くのメモリを消費しますか? もちろん。 それは悪いですか? 必ずしもそうではありません。 ライブラリを作成していて、強力な型安全性が必要な場合は、
@IntDefのような他のソリューションの代わりに列挙型を使用することを正当化
できます。 グループ化できる定数がたくさんある場合は、このリストを使用するのは賢明ではありません。 いつものように、決定を下す際に考慮する必要があるトレードオフがあります。
自動パッキング(自動ボックス化) -自動パッキングは、プリミティブ型からオブジェクト表現(たとえば、int-> Integer)への自動変換です。 プリミティブ型がオブジェクト表現に「パック」されるたびに、新しいオブジェクトが作成されます(ショック、私は知っています)。 多数ある場合、ガベージコレクターはより頻繁に起動します。 発生するオートパックの数を見逃すのは簡単です。なぜなら、私たちにとっては、プリミティブ型をオブジェクトに割り当てると自動的に発生するからです。 解決策-これらのタイプと一致するようにしてください。 アプリケーションのすべての場所でプリミティブ型を使用する場合は、不合理な自動パッケージングを避けてください。 メモリ分析ツールを使用して、プリミティブ型を表す多くのオブジェクトを見つけることができます。 トレースビューを使用して、Integer.valueOf()、Long.valueOf()などを探すこともできます。
HashMapとArrayMap / Sparse * Array-自動パッケージングの問題と同様に、HashMapを使用するには、オブジェクトをキーとして使用する必要があります。 アプリケーションでプリミティブ型「int」を使用し、HashMapとやり取りするときにIntegerに自動パックする場合、SparseIntArrayを使用できます。 オブジェクト形式のキーが必要な場合は、ArrayMapクラスを使用できます。 HashMapのように見えますが、
内部では動作が異なり 、メモリをより効率的に使用しますが、作業速度は低下します。 これらの2つのオプションのメモリはHashMapよりも少なくなりますが、HashMapよりもアイテムの取得やメモリの割り当てに時間がかかります。 要素数が1000未満の場合、プログラムを実行しても違いは目立たないため、キーと値のペアを使用する必要があるため、これらの要素は悪くありません。
コンテキスト認識 -前述のように、アクティビティでメモリリークが発生しやすくなります。 アクティビティがAndroidのリークの最も一般的なケースであることに驚かないかもしれません(!)。 また、これらのリークにはユーザーインターフェイスの階層全体が含まれているため、非常に高価であり、それ自体が多くのスペースを占有します。 多くのプラットフォーム操作にはContextオブジェクトが必要であり、通常はアクティビティを送信します。 このアクティビティで何が起こっているのかを理解してください。 リンクがキャッシュされ、このオブジェクトをアクティビティ自体より長く存続させ、このリンクを削除しない場合、メモリリークが発生します。
非静的な内部クラスは避けてください ;それらを初期化すると、暗黙的な非外部クラス参照が作成されます。 内側のクラスのオブジェクトのインスタンスが外側のクラスよりも長い時間を必要とする場合、外側のクラスは不要になってもメモリに残ります。 たとえば、AsyncTaskから継承したActivityクラスに非静的クラスを作成し、AsyncTaskを起動して、操作中にアクティビティを強制終了します。 このAsyncTaskは、作業が完了するまでこのアクティビティを保持します。 解決策はこれを行わず、必要に応じて静的内部クラスを宣言することです。
GPU分析(GPUプロファイリング)
GPUのレンダリングを分析するためのツールであるAndroid Studio 1.4への新しい追加。
Androidウィンドウで[GPU]タブに移動すると、画面上の各フレームのレンダリング時間を示すグラフが表示されます。

グラフ上の各バーは1つのレンダリングされたフレームを表し、色はレンダリングプロセス中のさまざまなフェーズを表します。
描画(青) -ビュー#onDraw()メソッドを表します。 この部分は、DisplayListオブジェクトを作成/更新します(Wikipediaでは、表示リスト(または表示ファイル)は、出力画像を定義する一連のグラフィックコマンドであり、GPUが理解できるOpenGLコマンドに後で変換されます。表示リストを作成するためのより多くの時間、または短時間で多くのビューがキャンセルされた場合。
準備(紫色) -Lollipopでは、UIスレッドがUIをより高速にレンダリングできるように別のスレッドが追加されました。 準備(紫色)-インターフェイススレッドがインターフェイスをより高速にレンダリングできるように、別のスレッドがLollipopに追加されました。 このスレッドはRenderThreadと呼ばれます。 彼はディスプレイリストをOpenGLコマンドに変換し、GPUに送信する責任があります。 この時点で、UIストリームは次のフレームの処理を続行できます。 このステップは、UIスレッドがすべてのリソースをRenderThreadスレッドに転送する時間を示します。 送信用のリソースが多い場合、たとえば、表示リストが多い場合や、表示リストが大きい場合は、この手順に時間がかかる場合があります。
プロセス(赤) -ディスプレイリストを実行してOpenGLコマンドを作成します。 多数のビューを再描画する必要があるため、完了する必要のある表示リストまたは複雑な表示リストが多い場合、このステップには時間がかかる場合があります。 ビューはキャンセルのために再描画されるか、オーバーレイビューがシフトされると表示されます。
実行(黄色) -OpenGLコマンドをGPUに送信します。 プロセッサはこれらのコマンドを含むバッファーをGPUに送信し、次のフレームのクリーンバッファーの受信を待機するため、この部分はブロッキング呼び出しです。 バッファの数には制限があり、GPUがビジー状態の場合、プロセッサはGPUが解放されるのを待ちます。 したがって、このステップで大きな値が表示される場合は、おそらく、GPUがインターフェイスを描画するのに忙しく、短時間で描画するのが難しすぎる可能性があることを意味します。
マシュマロでは、メジャー/レイアウト、入力処理などの新しいステップを示すために、より多くの色が追加されています。
しかし、あなたは、この機能を使用する前に、あなたが開発者向けオプションのGPUのレンダリングを有効にする必要があります。
我々が使用するので、これは、機器が必要な情報を得るために、ADBコマンドを使用できるようになります:ADBのシェルdumpsys gfxinfo <PACKAGE_NAME>我々は、すべてのデータそのものを取得することができますスケジュールを立てます。このコマンドは、階層内のビューの数、すべての表示リストのサイズなど、他の有用な情報を表示します。マシュマロでは、さらに多くの統計を見ることができます。
インターフェイスの自動テストがある場合は、特定の操作(リストのスクロール、重いアニメーションなど)の後にビルドサーバーにこのコマンドを実行させ、時間の経過とともに値に変化があるかどうか(たとえば、過剰なフレーム)を確認できます。これは、いくつかのコミットをプッシュした後のパフォーマンスの問題を特定するのに役立ち、アプリケーションが実稼働に入る前に問題を特定する時間が与えられます。ここで説明するように、「framestats」キーワードを使用して、より正確なレンダリング情報を取得することもできます。しかし、これがこのチャートを見る唯一の方法ではありません!「プロファイルGPUレンダリング」セクションの開発者のオプションで見たように、グラフを「画面上のストライプ」として表示するオプションがあります。これを含めると、画面上の各ウィンドウのグラフと、16ミリ秒のしきい値を決定する緑色のバーが表示されます。
右側の例では、いくつかのフレームが緑の線を越えています。つまり、フレームを描画するのに16ms以上かかりました。これらのストライプでは青色が優勢であるため、レンダリングには多くのビューがあるか、複雑であるか、またはその両方であることがわかります。このシナリオでは、さまざまなタイプのビューをサポートするフィードリストをスクロールしました。一部のビューは無効化され、一部のビューは他のビューよりもレンダリングが困難でした。一部のフレームがこのしきい値を超える理由として考えられるのは、現時点ではレンダリングが難しいビューがあるためです。階層ビューア
私はこのツールが大好きで、多くの人がまったく使用しないのが残念です!階層ブラウザを使用して、パフォーマンス統計を取得し、画面上の完全なビュー階層を表示し、すべてのビュープロパティにアクセスできます。テーマデータのダンプを取得して、各スタイル属性に使用されるすべての値を確認することもできますが、これは、Androidモニターからではなく、階層ブラウザーを個別のアプリケーションとして起動する場合にのみ使用できます。レイアウトを設計するとき、およびレイアウトを最適化するときに、このツールを使用します。
中央には、ビュー階層を表すツリーがあります。ビュー階層は広くなる場合がありますが、深すぎる(約10レベル)場合、価格は高価なレイアウト/計測フェーズになる可能性があります。ビューがビュー#onMeasure()で測定されるたびに、またはビュー#onLayout()にすべての子ビューがある場合、これらのコマンドは同じアクションを繰り返す子ビューに適用されます。 RelativeLayoutや一部のLinearLayout構成など、一部のレイアウトは各ステップを2回繰り返します。これらがネストされている場合、パスの数は指数関数的に増加します。右下に、各ビューの位置を示すレイアウトの「図面」が表示されます。ここまたはツリーでビューを選択し、左側にすべてのプロパティを表示できます。レイアウトを作成するときに、特定のビューが現在の場所になった理由がわからない場合があります。このツールを使用して、ツリーでそれを追跡し、それを選択してプレビューウィンドウのどこにあるかを確認することができます。他のビューが誤ってオーバーラップしているビューやその他のビューを見つけることができます。
各ビューについて、その測定/レイアウト/レンダリングの時間とすべての子ビューがあります。色は、ツリー内の他のビューと比較してこのビューがどのように表示されるかを示します。これは、最も弱いリンクを見つけるのに適した方法です。このビューのプレビューも表示されるので、作成の手順でツリー内を移動し、削除できる冗長な手順を見つけることができます。パフォーマンスに大きな影響を与えるものの1つは、オーバードローと呼ばれます。オーバーレイ
「GPUプロファイリング-実行フェーズ(グラフに黄色で表示)」セクションで見たように、GPUが画面上に多くのオブジェクトを描画する必要がある場合、完了に時間がかかり、各フレームのレンダリング時間が長くなります。たとえば、赤い背景に黄色のボタンがあるように、一方を他方の上に描画するとオーバーラップが発生します。 GPUは最初に赤い背景を描画し、次に黄色のボタンを描画して、オーバーレイを避けられないようにする必要があります。オーバーレイのレイヤーが多数ある場合、グラフィックプロセッサの動作が向上し、ターゲットから16ミリ秒離れます。
開発者オプションの「GPUオーバーレイのデバッグ」オプションを使用すると、すべてのオーバーレイに色が付けられ、この領域のオーバーレイの複雑さを示します。 1x / 2xオーバーレイは問題ありません。一部の明るい赤の領域でも問題ありませんが、画面に赤が多すぎる場合は問題になる可能性があります。いくつかの例を見てみましょう。
左の例では、リストは緑色で描かれていますが、これは通常は良好ですが、上部にオーバーレイがあり、赤になります。これはすでに問題です。右側の例では、リスト全体が赤で表示されています。どちらの場合も、2x / 3xオーバーレイの不透明なリストがあります。これらのオーバーレイは、アクティビティ/フラグメント、リスト、およびリストの各要素を含むウィンドウにフルスクリーンの背景色がある場合に使用できます。この問題は、そのうちの1つだけに背景色を設定することで解決できます。注:デフォルトのテーマは、ウィンドウのフルスクリーン背景色を宣言します。アクティビティに、画面全体を占める不透明なレイアウトがある場合、ウィンドウの背景を削除して、1つのオーバーレイレイヤーを削除できます。これは、サブジェクトまたはコードでgetWindow()を呼び出すことで実行できます。onCreate()のSetBackgroundDrawable(null)。階層ブラウザーを使用して、すべての階層レイヤーをPSDファイルにエクスポートし、Photoshopで開くことができます。Photoshopでさまざまなレイヤーを調べると、レイアウト内のすべてのオーバーレイが表示されます。この情報を使用して余分なオーバーレイを削除し、緑に満足せずに青を達成してください!アルファ
透明度を使用するとパフォーマンスに影響する可能性があります。その理由を理解するために、アルファビューを設定したときに何が起こるか見てみましょう。次のレイアウトを検討し
てください。3つのImageViewを重ねて表示したレイアウトが表示されます。直接/単純な実装では、setAlpha()コマンドを使用してアルファを設定すると、すべての子ビュー(この場合はImageViews)に渡されます。次に、これらのImageViewは、フレームバッファーにこのアルファ値で描画されます。結果:
これを見たくありませんでした。ImageViewはアルファでレンダリングされるため、すべてのオーバーレイ画像が混合されます。幸いなことに、オペレーティングシステムにはこの問題に対する解決策があります。レイアウトはオフスクリーンバッファにコピーされ、このバッファ全体にアルファが適用され、結果がフレームバッファにコピーされます。結果:
しかし、私たちはそれに代価を払った。フレームバッファーに描画する前に、オフスクリーンバッファーにビューを描画すると、さらに別の未定義のオーバーレイレイヤーが仮想的に追加されます。オペレーティングシステムは、このアプローチまたは前述の直接的なアプローチをいつ適用するかを正確に把握していないため、デフォルトではより複雑になります。ただし、アルファを設定し、画面バッファーの外に追加される複雑さを回避する方法はまだあります。- TextViews-setAlpha()の代わりにsetTextColor()を使用します。テキストの色にアルファチャネルを使用すると、このチャネルを使用してテキストを直接描画できます。
- ImageView-setAlpha()の代わりにsetImageAlpha()を使用します。TextViewと同じ理由。
- — , . , . hasOverlappingRendering() false, / . , onSetAlpha() true.
(Hardware Acceleration)
Honeycombでハードウェアアクセラレーションが導入されたとき、画面にアプリケーションを描画するための新しい描画モデルがありました。クイックレンダリング用のビュー描画コマンドを記述するDisplayList構造を導入しました。しかし、開発者が時々見逃したり正しく使用しない別のスーパー機能があります-ビューのレイヤービューレイヤーを使用して、オフスクリーンバッファーにビューを描画し(前に見たように、アルファチャネルを使用)、必要に応じて処理します。複雑なビューをより速くアニメーション化できるため、この機能は主にアニメーションに適しています。アニメーションのレイヤーがない場合、アニメーション化されたプロパティ(x座標、スケール、アルファ値など)を変更すると、ビューはそれをキャンセルします。複雑なビューの場合、このキャンセルはすべての子ビューに渡され、その後、子ビューが再描画され、高価な操作が行われます。ハードウェアが提供するビューレイヤーを使用して、ビューのテクスチャがGPUで作成されます。 x / y位置、回転、アルファなど、このテクスチャを無効化せずに適用できる操作がいくつかあります。これはすべてアニメーション中にキャンセルすることなく、複雑なビューをアニメーション化できることを!これにより、アニメーションがスムーズになります。これを行う方法のサンプルコードを次に示します。
本当に簡単?
はい。ただし、ハードウェアレイヤーを使用する際に留意すべき点がいくつかあります。
- 表示後のクリーンアップ -ハードウェアレイヤーは、メモリが限られているコンポーネントであるGPUのスペースを消費します。 アニメーションなどで必要な場合にのみ使用してみて、きれいにしてください。 上記のObjectAnimatorの例では、アニメーションの終了後にリスナーを使用してレイヤーを削除しました。 プロパティアニメーターの例では、withLayers()メソッドを使用しました。このメソッドは、アニメーションの最初にレイヤーを自動的に作成し、アニメーションの最後に削除します。
- ハードウェアレイヤーの適用後にビューを変更すると、ハードウェアレイヤーが無効になり、ビュー全体がオフスクリーンバッファーに再びレンダリングされます。 これは、ハードウェアレイヤーに最適化されていないプロパティを変更した後に発生します(回転、スケーリング、x / y、移動、回転点、アルファが最適化されました。ハードウェアレイヤーを更新するとオーバーヘッドが発生するため、使用する価値がない場合があります。
2番目の問題については、これらの更新をハードウェアレイヤーに表示する方法があります。 開発者オプションを使用して、「ハードウェアレイヤーの更新を表示」を有効にできます。

これをオンにすると、ハードウェアレイヤーの更新中にビューが緑色に点灯します。 ViewPagerが期待したほどスムーズにスクロールしなかったときに使用しました。 この開発者オプションを有効にした後、ViewPagerをスクロールしてみました。これは私が見たものです:

スクロール全体で、両方のページが緑色でした!
これは、ハードウェアレイヤーが作成され、ViewPagerのスクロール中にページがキャンセルされたことを意味します。 背景の視差効果を使用してページのスクロールを更新し、ページ上のオブジェクトを徐々にアニメーション化しました。 ただし、ViewPagerページのハードウェアレイヤーは作成しませんでした。 ViewPagerのソースコードを読んだ後、ユーザーがスクロールを開始した後、両方のページにハードウェアレイヤーが作成され、スクロールが停止すると削除されることがわかりました。
スクロール中にページのハードウェアレイヤーを作成することは理にかなっていますが、私にとっては悪いことでした。 通常、これらのページはViewPagerをスクロールしている間は変化せず、非常に複雑になる可能性があるため、ハードウェアレイヤーはページを十分にすばやく描画するのに役立ちます。 これは私のアプリケーションには当てはまらず、
私が書いた
小さなハックを使用してこのハードウェアレベルを削除する必要
がありました 。
ハードウェア層は特効薬ではありません。 それらがどのように機能し、どのように正しく使用するかを理解することが重要です。そうしないと、より重大な問題が発生する可能性があります。
自分でやる(DIY)
これらすべての例の準備中に、これらの状況をシミュレートするために多くのコードを書きました。 この
Githubリポジトリと
Google Playで重みを見つけることができます。 さまざまなシナリオをさまざまなアクティビティに分割し、このアクティビティを使用して見つけることができる問題の種類を理解するために、可能な限り文書化しました。 javadocアクティビティを読み、ツールを開いて、アプリで遊んでください。
追加情報
Android OSの開発に伴い、アプリケーションを最適化する方法が開発されています。 Android SDKで新しいツールが導入され、OSに新しい機能(ハードウェアレイヤーなど)が追加されました。 最新の変更を常に把握し、変更を行う前に妥協を考慮することが重要です。
YouTubeには、
Android Performance Patternsのスーパープレイリストがあり、
パフォーマンスに関連するさまざまなトピックを説明するGoogleエンジニアの短いビデオがたくさんあります。 さまざまなデータ構造の比較(HashMapとArrayMap)、ラスターイメージの最適化、さらにはネットワーククエリの最適化を見つけることができます。 それらをすべて見ることを強くお勧めします。
Google+ Android Performance Patternsコミュニティに参加して、Googleエンジニアを含む他のユーザーと生産性について話し合い、アイデア、記事、質問を共有してください。
より興味深いリンク:
- AndroidでのGraphics Architectureの仕組みをご覧ください。 Androidがインターフェイスを描画する方法、SurfaceFlingerなどのさまざまなシステムコンポーネント、およびそれらが相互に通信する方法についてのすべてがあります。 長い読み物ですが、それだけの価値があります。
- Google IO 2012との会話。描画モデルがどのように機能し、インターフェイスのレンダリング中にブレーキがどのように発生するかを示します。
- Android Performance WorkshopとDevoxx 2013の間の会話。Android4.4で作られた描画モデルの最適化をいくつか示し、パフォーマンスを最適化するためのさまざまなツール(システムトレース、オーバーレイなど)を示します。
- 予防的最適化に関する優れた記事と、それらが早期最適化とどのように異なるか。 多くの開発者は、コードの一部を最適化しないため、変更はマイナーだと考えています。 覚えておくべきことの1つは、すべてが折り畳まれている場合、 大きな問題が発生する可能性があることです。 重要でないと思われる小さな部分を最適化する機会がある場合は、これを無視しないでください。
- Androidのメモリ管理は、Google IO 2011の古いビデオであり、今でも関連しています。 Androidがアプリケーションのメモリを管理する方法と、Eclipse MATなどのツールを使用して問題を見つける方法を示しています。
- GoogleのエンジニアであるRomain Guyが作成した人気のtwitterクライアントの最適化事例の研究 。 このケーススタディでは、Romainがアプリケーションのパフォーマンスの問題を見つける方法と、それらを解決するために推奨することを示します。 同じアプリケーションのリワーク後の問題を示すフォローアップポストがあります。
今日、アプリケーションの最適化を開始するのに十分な情報と自信があることを願っています!
トレースを実行することで開始するか、適切な開発者オプションのいくつかを有効にします。
Udi Cohenによる投稿。元の
blog.udinic.com/2015/09/15/speed-up-your-app