携帯電話を実行する携帯電話の構成Android
は常に改善されていますが、それでもPC
携帯電話と比較することはできず、PC
携帯電話の大容量メモリと高性能を実現することはできませんCPU
。Android
そのため、アプリケーション開発時に無制限にメモリやメモリを使用することは不可能でありCPU
、CPU
メモリやメモリの使い方を誤ると、アプリケーションのフリーズやメモリオーバーフローなどの問題も発生します。
1 描画性能分析
Android
アプリケーションはクレンジング フェイスをユーザーに表示する必要があり、ユーザーはクレンジング フェイスと対話するため、インターフェイスの流暢さが非常に重要です。
1.1 描画原理
View
3
の描画プロセスには、 、measure
、の 3 つのステップがありlayout
、draw
主にシステムのアプリケーション フレームワーク層で実行されますが、画面へのデータの実際のレンダリングはNative
システム層のSurfaceFlinger
サービスによって行われます。
描画処理は主に、ラスタライズとレンダリングを担当する 、 、 のデータ計算作業CPU
によって実行さMeasure
れLayout
ます。および(グラフィックス プロセッサ) はグラフィックス ドライバー層を介して接続されており、グラフィックス ドライバー層はキューを維持し、キューに追加されるため、キューからデータを取り出すことができます。Record
Execute
GPU
CPU
GPU
graphics processing unit
CPU
display list
GPU
描画性能に関して言えば、フレームの概念について言及する必要があります。フレーム数は、時間内に送信される画像の量であり、グラフィックス プロセッサが 1 秒間に何回リフレッシュできるか( ( )1s
で表される) としても理解できます。各フレームは実際には静止画であり、フレームを素早く連続して表示することによって、動いているような錯覚が生まれます。最も単純な例は、ゲームをプレイしているときに、画面が にある場合は行き詰まりを感じませんが、画面が より低い場合、例えば に行き詰まりを感じることです。FPS
Frames Per Second
60fps
60fps
50fps
これは、人間の脳が目で見た情報を継続的に受信して処理するためであり、単位時間あたりに処理されるフレーム数が多ければ多いほど、脳が認識できる最小フレーム数がロードされるためです。このとき、10fps ~ 12fps
脳の イメージが静止しているのか変化しているのかは不明です。
画面を に保つためには60fps
、 以内に画面を1 回1s
更新する必要があります60
。つまり、16.6667ms
1 回更新しない (描画時間が16ms
以内) 必要があります。
Android
システムは、のレンダリングをトリガーするために信号16ms
を送信します。すべてのレンダリングが成功すると、滑らかな画像に必要なものを達成できます。(Vertical Synchronization)の略で、タイミング割り込みの一種で、信号を受信するとフレームごとのデータ処理を開始します。操作のコストが高すぎて、システムが信号を受け取ったときに正常にレンダリングできない場合、フレーム ドロップが発生します。ユーザーには、同じフレームが で表示されます。VSYNC
UI
60fps
VSYNC
VSYNC
Vertical Synchronization
VSYNC
CPU
24ms
VSYNC
32ms
吃音の原因はさまざまですが、主なものとしては以下のようなものがあります。
- レイアウト
Layout
が複雑すぎて16ms
内でレンダリングできません。 - 同時に実行されるアニメーションが多すぎると、過負荷が発生する
CPU
か、GPU
オーバーロードが発生します。 View
オーバードロー。同じフレーム時間内に一部のピクセルが複数回描画されます。UI
少し時間のかかる操作がスレッド内で実行されました。GC
一時停止時間が長すぎるか、リサイクル中にGC
大量の一時停止時間が頻繁に発生します。
1.2 ツール
1.2.1 GPU レンダリングのプロファイル
プロファイル GPU レンダリングは、Android 4.1
システムによって提供される開発支援機能であり、開発者向けオプションでオンにすることができます: [設定] –> [開発者向けオプション] –> [GPU レンダリング モード分析] –> [画面上に棒グラフとして表示]:
図の横軸は時間を表し、縦軸は特定のフレームの消費時間を表します。緑色の横線は警告線です。この線を超えると継続時間を超過したことを意味します。縦方向のカラー ヒストグラムが維持されていることを確認してください16ms
。緑の線の下。これらの縦方向の色付きヒストグラムはフレームを表し、異なる色の色付きヒストグラムは異なる意味を表します。
- オレンジ色は処理時間を表し、フレームのレンダリング
CPU
を指示します。コマンドへの応答を待機するGPU
ため、これはブロック呼び出しです。オレンジ色のヒストグラムが高い場合は、非常にビジーであることを意味します。CPU
GPU
GPU
- 赤は実行時間を表し、レンダリング
Android
にかかる時間を表します。赤のヒストグラムが高い場合は、ビューの再送信が原因である可能性があります。赤いヒストグラムが高くなる複雑なカスタマイズもあります。2D
Display List
View
- 青は、図面の測定時間、つまり の作成と更新にかかる時間を表します
Display List
。青色のヒストグラムが高い場合は、再描画する必要があるか、View.onDraw()
メソッドが処理する処理が多すぎる可能性があります。
インターフェイスが更新されると、インターフェイスには各フレームのレンダリング時間がリアルタイム ヒストグラムで表示されます。ヒストグラムが高いほど、レンダリング時間が長くなります。各ヒストグラムには、ベンチマークを表す緑色の水平線があります。マークされた列の行には次の内容が含まれ16ms
ます3 つの部分 (青はDisplay List
描画の測定時間を表し、赤はOpenGL
レンダリングDisplay List
に必要な時間を表し、黄色は処理のCPU
待機時間を表しますGPU
)。各フレームの合計時間がベースラインより低い限り、UI
カードトンは存在しません。問題があります (実際、それらの一部がベースラインを超えていても問題はありません)。
1.2.2GPU
描画
パフォーマンスを最適化するために、UI
開発者向けオプションのGPU
オーバードロー ツールを使用して分析することもできます。[設定] -> [開発者向けオプション] -> [デバッグGPU
オーバードロー] でデバッグをオンにすると (デバイスごとに場所や名前が異なる場合があります)、次の図が表示されます (settings
現在のインターフェイスのオーバードローを分析します)。
以下の手順:
青 (1x
上書き)、薄緑 (2x
上書き)、薄赤 (3x
上書き)、濃い赤 (上書き4x
) は4
さまざまなレベルのOverdraw
状態を表します。目標は、赤を最小限に抑えOverdraw
、より多くの青の領域を表示することです。
Overdraw
UI
レイアウトの重複部分が多いことが原因である場合もあれば、不必要に重複している背景が原因である場合もあります。たとえば、あるActivity
オブジェクトには背景があり、その中のオブジェクトにもLayout
独自の背景があり、同時にそれぞれの子にもView
独自の背景があります。不要な背景画像を削除するだけで、多くの赤いOverdraw
領域が減り、青い領域の割合が増加します。この対策により、プログラムのパフォーマンスが大幅に向上します。
レイアウトで両方を使用できる場合はRealtiveLayout
、LinearLayout
それを直接使用しますLinearLayout
。これは、Relativelayout
のレイアウトがより複雑で、CPU
描画に時間がかかるためです。複数LinearLayout
またはFramelayout
入れ子が必要な場合は、それを使用できますRelativelayout
。マルチレベルのネストのため、レイアウトの描画の大部分が繰り返されるため、プログラムのパフォーマンスが低下します。
2 レイアウト最適化ツール - Layout Inspector
レイアウト インスペクター ツールとレイアウト検証ツールを使用してレイアウトをデバッグする
3 レイアウト最適化手法
レイアウトを最適化する方法は数多くありますが、主にレイアウト、include
、merge
の合理的な使用が挙げられますViewStub
。
3.1 レイアウトの合理的な使用
一般的に使用されるレイアウトには、主にLinearLayout
、RelativeLayout
、FrameLayout
などが含まれます。これらを適切に使用すると、Android
描画の負荷が軽減され、パフォーマンスが向上します。例えば:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="布局优化" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Merge" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ViewStub" />
</LinearLayout>
</LinearLayout>
レイアウトには 3 つのレイヤーがあることがわかります。
レイアウトには合計 のレイヤーがあり3
、合計5
が含まれていますView
。RelativeLayout
で書き換えると、コードは次のようになります。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="布局优化" />
<TextView
android:id="@+id/tv_text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/tv_text1"
android:text="Merge" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/tv_text1"
android:layout_below="@+id/tv_text2"
android:text="ViewStub" />
</RelativeLayout>
レイアウトには 2 つのレイヤーがあることがわかります。
2
レイアウトにはレイヤーがあり、合計4
でがあり、このことから1レイヤーのレイアウトが削減されていることView
がわかります。RelativeLayout
レイアウトが複雑な場合、RelativeLayout
レイアウトのレベルを下げるために合理的に使用できます。の配置は相互に依存することに基づいているため、RelativeLayout
よりもパフォーマンスが低くなります。LinearLayout
RelativeLayout
View
しかし、実際の開発プロセスではさまざまな状況に直面するため、どちらのパフォーマンスが優れているとは簡単には言えません。一般に、レイアウト層が多い場合に使用することをお勧めします。RelativeLayout
また、入れ子のレイアウトが多い場合に使用することをお勧めしますLinearLayout
。
3.2include
レイアウトの再利用にタグを使用する
複数のレイアウトが同じレイアウト (1 つなど) を再利用する必要がある場合TitleBar
、これらの清掃面に同じレイアウトを追加する必要がある場合TitleBar
、保守が非常に面倒になり、TitleBar
追加する必要がある各清掃面にレイアウトをコピーする必要があります。省略が起こりやすいものです。変更する場合は、参照されているレイアウト内で変更するTitleBar
必要があります。TitleBar
これらの問題を解決するには、include
ラベルを使用して解決できます。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:src="@drawable/ic_launcher_background"
android:padding="3dp" />
</LinearLayout>
これはとTitleBar
で構成されます。以前に使用したレイアウトに次のように導入してみましょう。ImageView
TextView
TitleBar
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/title_bar" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="布局优化" />
<TextView
android:id="@+id/tv_text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/tv_text1"
android:text="Merge" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_text2"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/tv_text1"
android:text="ViewStub" />
</RelativeLayout>
</LinearLayout>
レイアウトには 2 つのレイヤーがあることがわかります。
3.3merge
ラベルを使用して冗長なレイヤーを削除する
merge
これはマージを意味し、適切なシナリオでタグを使用すると、merge
冗長なレイヤーを減らすことができます。merge
タグは通常、include
タグと組み合わせて使用されます。前のセクションの例では、merge
代わりに タグが使用されている場合LinearLayout
、コードは次のようになります。
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:padding="3dp"
android:src="@drawable/ic_launcher_background" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="绘制优化" />
</merge>
レイアウト階層:
以前のものはなくなっていることがわかりますが、 を置き換えるタグがLinearLayout
あるため、 が失敗し、レイアウトがめちゃくちゃになります。ラベルを置き換えるか、同じレイアウト方向にすることをお勧めします。たとえば、現在の親レイアウトのレイアウト方向が垂直で、含まれる子レイアウトのレイアウト防御線も垂直であるため、ラベルを使用できます。ただし、このシナリオではと のレイアウト方向は水平であり、明らかにこの要件を満たしていません。merge
LinearLayout
LinearLayout
merge
FrameLayout
LinearLayout
LinearLayout
LinearLayout
merge
TitleBar
LinearLayout
3.4ViewStub
読み込み速度を向上させるために使用する
一般的な開発シナリオでは、特定のレイアウト上のすべてのコントロールが表示されるのではなく、その一部が表示されることがあります。この場合、一般的な方法は と 属性を使用することです。この方法は効率的ではありませんが、非表示の目的は達成さView
れGONE
ますVISIBLE
。 、しかし、それらはまだレイアウト内にあり、システムは引き続きそれらを解析し、ViewStub
この問題を解決するために使用できます。
ViewStub
軽量でView
目に見えず、レイアウトスペースを取りません。ViewStub
メソッドの呼び出しまたは設定が表示されるとinflate
、システムはViewStub
指定されたレイアウトをクランプし、メソッドの呼び出しまたは設定が表示されるViewStub
前にこのレイアウトを に追加します。レイアウト スペースやシステム リソースを占有しません。その主な目的は、ターゲットビューが位置を占めます。したがって、これを使用すると、クレンジング初期化のパフォーマンスが向上し、インターフェイスの読み込み速度が向上します。まず、レイアウトにタグを追加します。ViewStub
inflate
ViewStub
ViewStub
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ViewStub
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout="@layout/title_bar" />
</LinearLayout>
タグ内でViewStub
使用して、android:layout
以前に作成したレイアウトを参照しますtitle_bar.xml
。プログラムを実行すると、ViewStub
ラベルで参照されるレイアウトは表示できません。これは、レイアウトが にロードされておらずViewStub
、コードで使用されていないためですViewStub
。
public class MainActivity extends AppCompatActivity {
private ViewStub viewStub;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewStub = findViewById(R.id.view_stub);
viewStub.inflate(); // 1
viewStub.setVisibility(View.VISIBLE); // 2
}
}
コメント1
とコメントは、アプリケーションのレイアウトを に配置するため2
に使用され、アプリケーションのレイアウトが表示されます。を使用する場合は、次の問題に注意してください。ViewStub
ViewStub
ViewStub
ViewStub
ロードできるのは 1 回のみで、ViewStub
ロード後はオブジェクトは空になるため、で参照されるレイアウトがロードされた後は、参照されるレイアウトを制御するためにViewStub
使用することはできません。したがって、コントロールを継続的に表示および非表示にする必要がある場合でも、属性ViewStub
を使用する必要があります。View
Visibility
ViewStub
タグをネストすることはできませんmerge
。ViewStub
操作はレイアウト ファイルです。特定の を操作したいだけの場合は、属性View
を使用する必要があります。View
Visibility
3.5 描画の最適化
描画の最適化は主に、View.onDraw
メソッドが多数の操作の実行を回避する必要があることを意味します。
onDraw
onDraw
このメソッドはリアルタイムで実行されるため、新しいローカル オブジェクトを作成する必要はありません。その結果、多数の一時オブジェクトが生成され、メモリ使用量が増加し、システムは常に実行されているため、実行効率が低下しますGC
。onDraw
このメソッドでは時間のかかる操作を実行する必要がなく、ループには多くの時間onDraw
がかかるため、メソッド内でのループの使用量は少なくなりますCPU
。描画が滑らかでなくなったり、カクついたりするなどの原因となります。View
Google は、描画フレーム レートが で安定していることを公式に指摘しており、各フレームの描画時間が( )60dps
を超えないようにする必要があります。保証するのは難しいですが、できる限り減らす必要があります。16ms
1000/60
60dps
これは、現時点で最適な画像表示速度であり、Android
ほとんどのデバイスで設定されているデバッグ頻度でもあります。インターフェイスのリフレッシュ操作が正常に完了する16ms
と、滑らかな画像を表示できますがVSYNC
、何らかの理由でリフレッシュ操作ができない場合があります。信号を受信すると完了しますので、フレームドロップが発生し、当然リフレッシュフレームレートも低下します(リフレッシュフレームレートが通常から低レベルに60fps
低下する30fps
。