Android アプリケーションを開発する場合、パフォーマンスの最適化は非常に重要な側面です。最適化により、アプリケーションの応答速度が向上し、遅延が減少し、アプリケーションの滑らかさが向上するため、ユーザー エクスペリエンスが向上する一方で、最適化によりアプリケーションのリソース使用量が削減され、アプリケーションのパフォーマンスが向上します。アプリケーションの安定性とセキュリティを向上させ、アプリケーションのコストや強制終了の可能性を削減し、それによってユーザーの満足度と維持率を向上させます。
ただし、多くの開発者にとって、Android のパフォーマンスの最適化は難しい問題であることがよくあります。Android デバイスにはさまざまな種類があり、ハードウェア構成が異なるため、最適化の方法や戦略も異なります。同時に、Android アプリケーションの開発サイクルは長く、継続的な反復と更新が必要になることが多いため、最適化も継続して最適化する必要があります。
Android パフォーマンス最適化の知識とスキルを学ぶことは、すべての Android 開発者にとって不可欠なスキルの 1 つです。Android パフォーマンス最適化の基本原則と方法を習得することで、Android デバイスの動作メカニズムをより深く理解し、アプリケーションのパフォーマンスのボトルネックを理解して、アプリケーションのパフォーマンスと安定性を向上させるための効果的な最適化戦略と対策を採用し、パフォーマンスを向上させることができます。ユーザーの満足度、維持率。
この記事では、開発者が Android デバイスの動作原理をより深く理解し、Android パフォーマンス最適化の基本的な方法とテクニックを習得できるように、Android パフォーマンス最適化の基本原理、最適化戦略、実践的なテクニックを紹介します。これにより、アプリケーションのパフォーマンスと安定性が向上し、ユーザーに提供します。より滑らかな体験を提供します。
Android のパフォーマンス最適化に関する問題は多岐にわたりますが、一般的な問題のいくつかを以下に示します。
-
メモリ リーク: メモリ リークは、アプリケーションがメモリを不適切に管理し、過度のメモリ使用を引き起こしたり、アプリケーションのクラッシュを引き起こしたりするときに発生します。
-
レイアウトの最適化: レイアウトは、アプリケーションで最も一般的なパフォーマンスのボトルネックの 1 つです。レイアウトが複雑すぎると、アプリケーションの応答が遅くなったり、フリーズしたりする可能性があるためです。
-
画像の最適化: 画像はアプリケーション内で最もメモリを大量に消費するリソースの 1 つであるため、アプリケーションのパフォーマンスを確保するには、慎重に使用し、適切に圧縮してキャッシュする必要があります。
-
ネットワーク リクエストの最適化: ネットワーク リクエストはアプリケーションで多くの時間とリソースを消費する可能性があるため、リクエストの数を減らし、応答速度を向上させるためにネットワーク リクエストを最適化する必要があります。
-
データベースの最適化: アプリケーションがデータベースへの大量のアクセスを必要とする場合、パフォーマンスの問題が発生する可能性があります。データベース設計を最適化し、適切なデータベース キャッシュを使用することで、アプリケーションのパフォーマンスを向上させることができます。
-
マルチスレッドの最適化: マルチスレッドはアプリケーションのパフォーマンスを向上させることができますが、使い方を誤ると、デッドロック、スレッド競合、その他の問題が発生する可能性があります。
-
メモリの最適化: メモリはアプリケーションのパフォーマンスにおける重要な要素の 1 つです。不要になったメモリをすぐに解放し、不必要なメモリ割り当てを回避することで、アプリケーションのパフォーマンスを向上させることができます。
-
コードの最適化: コードの構造とアルゴリズムを最適化すると、アプリケーションのパフォーマンスを向上させることができます。たとえば、より高速で効率的なデータ構造とアルゴリズムを使用して、アプリケーションの応答性を向上させます。
-
セキュリティの最適化: セキュリティの問題は、アプリケーションのパフォーマンスに悪影響を与える可能性もあります。安全でないコーディング手法を回避し、暗号化を使用してデータを保護することで、アプリケーションのセキュリティとパフォーマンスを向上させることができます。
Android のパフォーマンスの最適化は、つまるところメモリの問題であり、メモリ レベルの最適化は、説明で説明されている従来の最適化項目だけでなく、ディスクの読み取りと書き込みの数、ディスク ページ データの同期などをさらに最適化することもできます。
1. メモリリーク
メモリ リークとは、アプリケーションが動作中に使用されなくなったメモリ リソースを正しく解放できないことを指します。その結果、メモリ使用量が継続的に増加し、最終的にはアプリケーションがクラッシュしたり、動作が遅くなったりします。
メモリリークの原理
Android のメモリ リークの原理は、アプリケーションがプログラミング上の問題やエラーによりメモリを使用すると、使用されなくなったメモリを解放できなくなり、最終的にシステム内のメモリが不足し、システムの安定性とパフォーマンスに影響を与えることを意味します。 。
Android のメモリ リークを引き起こす可能性のある一般的な原因は次のとおりです。
オブジェクト参照が解放されていません
オブジェクトが作成されたときに、それらが正しく解放されないと、アプリケーションが終了するまで、これらのオブジェクトはメモリを占有します。たとえば、アクティビティが破棄されたときに、アクティビティが他のオブジェクトへの参照を保持している場合、それらのオブジェクトはガベージ コレクタによってリサイクルできず、メモリ リークが発生します。
メモリ リークが発生すると、これらのメモリ内のオブジェクトが参照され、ガベージ コレクション メカニズムによってリサイクルできなくなります。このとき、GCRoot を使用してメモリ リークしたオブジェクトと参照を特定する必要があります。
GCRoot は、ガベージ コレクション メカニズムのルート ノードです。ルート ノードには、仮想マシン スタック、ローカル メソッド スタック、メソッド領域のクラス静的属性参照、アクティブなスレッドなどが含まれます。これらのオブジェクトは、ガベージ コレクションによって「生きているオブジェクト」と見なされます。回収メカニズムがあり、リサイクルされません。
ガベージ コレクション メカニズムが実行されると、GCRoot から開始され、すべてのオブジェクト参照を走査し、すべての生きたオブジェクトにマークが付けられます。マークされていないオブジェクトはガベージ オブジェクトであり、リサイクルされます。
メモリ リークが発生すると、ガベージ コレクション メカニズムは、使用されなくなった一部のオブジェクトをリサイクルできません。これらのオブジェクトは引き続き参照され、GCRoot からメモリ リーク オブジェクトへの参照チェーンが形成されます。これらのオブジェクトはリサイクルされず、メモリ リークが発生します。
メモリ リーク オブジェクトと GCRoot の間の参照チェーンを見つけることで、メモリ リークの原因を特定し、メモリ リークの問題を解決できます。LeakCancry はこのメカニズムを通じて実装されています。一般的な GCRoot には次のようなものがあります。
-
仮想マシン スタックで参照されるオブジェクト (ローカル変数)。
-
メソッド領域の静的プロパティ(静的変数)によって参照されるオブジェクト。
-
JNI によって参照されるオブジェクト。
-
Java スレッド (Thread) によって参照されるオブジェクト。
-
Java の同期ロックによって保持されるオブジェクト。
匿名内部クラスによるメモリリーク
匿名内部クラスは通常、外部クラスへの参照を保持します。外部クラスのライフサイクルが匿名内部クラスのライフサイクルよりも長い場合、(訂正、ライフサイクルの使用はここでは適切ではありません。外部クラスが破棄されると、内部クラスはクラスは自動的に破棄されません。内部クラスは外部クラスのメンバー変数ではなく、外部クラスのスコープ内で作成された単なるオブジェクトであるため、内部クラスの破棄タイミングと外部クラスの破棄タイミングは異なります。クラスが異なるため、対応するオブジェクトが存在するかどうかに依存します (保持された参照) により、外部クラスがリサイクルできなくなり、メモリ リークが発生します。
静的変数はアクティビティまたはコンテキストへの参照を保持します
静的変数がアクティビティまたはコンテキストへの参照を保持している場合、これらのアクティビティまたはコンテキストはガベージ コレクターによってリサイクルできず、メモリ リークが発生します。
閉じられていないカーソル、ストリーム、またはビットマップ オブジェクト
Cursor、Stream、または Bitmap オブジェクトを使用するときにプログラムがこれらのオブジェクトを適切に閉じないと、これらのオブジェクトがメモリを占有し続け、メモリ リークが発生します。
リソースが解放されていない
データベース接続を閉じない、オーディオ リソースを解放しないなど、システム リソースの使用時にプログラムがシステム リソースを正しく解放しない場合、これらのリソースはメモリを占有し続け、メモリ リークが発生します。
よくあるメモリリーク
静的参照によるメモリ リーク
オブジェクトが静的変数で保持されている場合、そのオブジェクトが使用されなくなっても、ガベージ コレクターによってリサイクルされないため、メモリ リークが発生します。
public class MySingleton {
private static MySingleton instance;
private Context context;
private MySingleton(Context context) {
this.context = context;
}
public static MySingleton getInstance(Context context) {
if (instance == null) {
instance = new MySingleton(context);
}
return instance;
}
}
上記のコードでは、MySingleton は Context オブジェクトへの参照を保持しており、MySingleton は静的変数です。つまり、オブジェクトが使用されなくなった場合でも、ガベージ コレクターによってリサイクルされません。
注: 静的変数を使用する必要がある場合は、メモリが時間内に解放されるように、必要がないときは静的変数を null に設定するように注意してください。
匿名内部クラスによるメモリリーク
匿名内部クラスは外部クラスへの参照を暗黙的に保持します。匿名内部クラスが保持されている場合、外部クラスはガベージ コレクションされません。
public class MyActivity extends Activity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
button = new Button(this);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// do something
}
});
setContentView(button);
}
}
匿名の内部クラス OnClickListener は、外部クラス MyActivity への参照を保持します。MyActivity が破棄される前にボタンがクリアされない場合、MyActivity はガベージ コレクションされません。(ここでは、ボタンを自分で定義したオブジェクトとみなすことができます。一般的な解決策は、ボタン オブジェクトを空に設定することです)
注: アクティビティが破棄されると、アクティビティ参照を保持するすべてのオブジェクトが null に設定される必要があります。
ハンドラーによるメモリリーク
Handler は Android アプリケーションでよく使われるスレッド通信機構で、使い方を誤るとメモリリークの原因となります。
public class MyActivity extends Activity {
private static final int MSG_WHAT = 1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_WHAT:
// do something
break;
default:
super.handleMessage(msg);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.sendEmptyMessageDelayed(MSG_WHAT, 1000 * 60 * 5);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 在Activity销毁时,应该将Handler的消息队列清空,以避免内存泄漏。
mHandler.removeCallbacksAndMessages(null);
}
}
ハンドラーはアクティビティへの参照を保持します。アクティビティが破棄される前にハンドラーのメッセージ キューに未処理のメッセージがある場合、アクティビティはガベージ コレクションされません。
注: アクティビティが破棄されるときは、メモリ リークを避けるためにハンドラーのメッセージ キューをクリアする必要があります。
ビットマップオブジェクトによるメモリリーク
Bitmap オブジェクトは作成されると大量のメモリを占有するため、解放が間に合わないとメモリ リークが発生します。
public class MyActivity extends Activity {
private Bitmap mBitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 加载一张大图
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.big_image);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 释放Bitmap对象
mBitmap.recycle();
mBitmap = null;
}
}
アクティビティが破棄されると、ビットマップ オブジェクト mBitmap が時間内に解放される必要があります。そうしないと、メモリ リークが発生します。
注: 多数のビットマップ オブジェクトを使用する場合、メモリ リークを避けるために、未使用のオブジェクトを適時にリサイクルする必要があります。さらに、Glide、Picasso などのイメージ読み込みライブラリを使用してビットマップ オブジェクトを管理することも検討できます。
リソースが閉じられていないことが原因でメモリ リークが発生する
ファイル、データベースなどの一部のシステム リソースを使用するときに、それらが時間内に閉じられないと、メモリ リークが発生する可能性があります。例えば:
public void readFile(String filePath) throws IOException {
FileInputStream fis = null;
try {
fis = new FileInputStream(filePath);
// 读取文件...
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
上記のコードでは、ファイルの読み取り後に FileInputStream オブジェクトが時間内に閉じられないと、メモリ リークが発生する可能性があります。
注: ファイル、データベースなどの一部のシステム リソースを使用する場合、メモリ リークを避けるために、関連するオブジェクトを時間内に閉じる必要があります。
メモリ リークを回避するには、コードを記述するときに常に注意し、使用されなくなったオブジェクトを速やかにクリーンアップし、メモリ リソースが適切なタイミングで解放されるようにする必要があります。同時に、Android Profiler、LeakCanary などのいくつかのツールを使用してメモリ リークの問題を検出できます。
WebView のメモリ リーク
WebViewを使用する場合、解放が間に合わないとメモリリークが発生する可能性があります。
public class MyActivity extends Activity {
private WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = findViewById(R.id.webview);
mWebView.loadUrl("https://www.example.com");
}
@Override
protected void onDestroy() {
super.onDestroy();
// 释放WebView对象
if (mWebView != null) {
mWebView.stopLoading();
mWebView.clearHistory();
mWebView.clearCache(true);
mWebView.loadUrl("about:blank");
mWebView.onPause();
mWebView.removeAllViews();
mWebView.destroy();
mWebView = null;
}
}
}
上記のコードでは、Activity が破棄されたときに、WebView オブジェクトを時間内に解放する必要があります。そうしないと、メモリ リークが発生する可能性があります。
注: WebView を使用するときは、WebView オブジェクトを適切なタイミングで解放する必要があります。Activity が破棄されるときに、WebView の destroy メソッドを呼び出すことができます。同時に、WebView の履歴、キャッシュなどをクリアして、すべてのアクティビティが確実に実行されるようにする必要があります。リソースが解放されます。
監視ツール
-
メモリ監視ツール: Android Studio は、開発プロセス中にアプリケーションのメモリ使用量をリアルタイムで監視できるメモリ監視ツールを提供し、開発者がメモリ リークをタイムリーに発見できるようにします。
-
DDMS: Android SDK の DDMS ツールは、メモリ使用量、スタック トレース、その他の情報を含む、Android デバイスまたはエミュレータのプロセスとスレッドを監視し、メモリ リークの診断に使用できます。
-
MAT: MAT (Memory Analyzer Tool) は、アプリケーションのヒープ メモリ使用量を分析し、メモリ リークを特定して特定できる Eclipse ベースのメモリ分析ツールです。
-
Tencent の Matrix も非常に優れたオープンソース プロジェクトであり、誰でも使用することをお勧めします。
2. まとめ
メモリ リークとは、プログラム内の特定のオブジェクトやリソースが適切に解放されず、その結果メモリ使用量が増加し、最終的にはアプリケーションのクラッシュやシステム動作の低下につながる可能性があることを意味します。
一般的なメモリ リークの問題には次のようなものがあります。
-
Activity または Fragment オブジェクトを長時間保持することによって発生するメモリ リーク。
-
匿名内部クラスと非静的内部クラスによって引き起こされるメモリ リーク。
-
WebView が Activity オブジェクトを保持することによって発生するメモリ リーク。
-
リソース オブジェクトをシングルトン モードで保持することによって発生するメモリ リーク。
-
リソースが閉じられていないことが原因でメモリ リークが発生します。
-
Context オブジェクトを保持する静的変数によって引き起こされるメモリ リーク。
-
外部クラス参照を保持するハンドラーによって発生するメモリ リーク。
-
ビットマップが大量のメモリを占有することによって引き起こされるメモリ リーク。
-
大量のデータを保持するシングルトンによって発生するメモリ リーク。
メモリ リークの問題を回避するには、次の措置を講じることができます。
-
Activity または Fragment オブジェクトを直ちに解放します。
-
匿名内部クラスと非静的内部クラスは避けてください。
-
WebView を使用する場合は、すぐに destroy メソッドを呼び出します。
-
シングルトン モードでリソース オブジェクトを長時間保持することは避けてください。
-
リソース オブジェクトをすぐに閉じます。
-
Context オブジェクトを保持する静的変数は避けてください。
-
ハンドラーが外部クラス参照を保持するのを避けます。
-
ビットマップを使用する場合は、適時にメモリを解放してください。
-
大量のデータを保持するシングルトンは避けてください。
上記は Android のパフォーマンス最適化についてまとめたものですが、メモリ リークのシナリオは異なりますし、最適化方法も一意ではありませんので、ぜひ一緒に議論してください。