著者:ルー 誰もが知っています
アプリが起動の最適化を実行しているとき、アプリケーションは初期化作業を行いますが、アプリケーション内で時間のかかる操作は行いません。ただし、一部の初期化作業には時間がかかる場合があります。初期化操作は、子スレッドを開始することで完了できます。
実行時間を計算する
- 従来ソリューション(手動埋め込みポイントマーク)
- AOPの入手方法
1. 従来プラン
従来の解決策では、実行前に開始時刻をマークし、実行後に終了時刻をマークし、開始時刻と終了時刻の差を計算することで、その時間差が所要時間となります。
具体的な時間計算の実装は以下のコードで示されていますが、Application の onCreate メソッド内で多数の初期化メソッドが呼び出されますので、手動でマークを埋めて各メソッドの時間を計算しています。
//Application.java
@Override
public void onCreate() {
initSharedPreference();
initImageLoader();
initSQLite();
//.....
}
private void initSharedPreference() {
long startTime = System.currentTimeMillis();
//init SharedPreference
Log.d(TAG, "initSharedPreference cost :" + (System.currentTimeMillis() - startTime));
}
private void initImageLoader() {
long startTime = System.currentTimeMillis();
Fresco.initialize(this);
Log.d(TAG, "initImageLoader cost :" + (System.currentTimeMillis() - startTime));
}
private void initSQLite() {
long startTime = System.currentTimeMillis();
//init bugly
Log.d(TAG, "initSQLite cost :" + (System.currentTimeMillis() - startTime));
}
上記の計算方法は考えやすい実装方法ですが、デメリットも明らかです。
- 各メソッドのマーク付けと計算には時間がかかり、コードは十分に洗練されていません。
- プロジェクトにとって非常に侵襲的であり、多くの作業が必要です。
2.AOPの入手方法
AOP は私たちがよく言うものであり、それを目指す
面向切面编程
ことができます。同一类问题
统一处理
次に、AOP を使用して、アプリケーションの各メソッドの実行時間をエレガントに取得します。
2.1 AspectJ の紹介
Android で AspectJ ライブラリを通じて AOP を使用し、このライブラリを導入します。
- プロジェクトルート build.gradle に AspectJ プラグインを導入します。
- クラスパス「com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4」
- モジュール内の Build.gradle アプリケーション プラグイン
- プラグインを適用: 'android-aspectjx'
- モジュールのbuild.gradleにaspectjライブラリを導入
- 実装「org.aspectj:aspectjrt:1.8.9」
2.2 AOP の具体的な使用法
- @Aspect アノテーションを使用してクラス PerformanceAop を定義します
- @Around("execution(* com.lu.aop.MyApplication.**(...))") は、MyApplication の各メソッドをフックする必要があることを示します。
- メソッドの実行前後のタイムスタンプを記録し、対応する時間差を計算します。
@Aspect
public class PerformanceAop {
private static final String TAG = PerformanceAop.class.getSimpleName();
@Around("execution(* com.lu.aop.MyApplication.**(..))")
public void getTime(ProceedingJoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
//得到方法的名字,例如:MyApplication.attachBaseContext(..)
String name = signature.toShortString();
//记录方法执行前的时间戳
long startTime = System.currentTimeMillis();
try {
//执行该方法
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
//记录执行该方法的时间
long costTime = System.currentTimeMillis() - startTime;
Log.e(TAG, "method " + name + " cost:" + costTime);
}
}
运行结果
2019-08-18 17:09:12.946 10094-10094/com.lu.aop E/PerformanceAop: method MyApplication.attachBaseContext(..) cost:1
2019-08-18 17:09:12.979 10094-10094/com.lu.aop E/PerformanceAop: method MyApplication.initSQLite() cost:11
2019-08-18 17:09:13.002 10094-10094/com.lu.aop E/PerformanceAop: method MyApplication.initImageLoader() cost:17
2019-08-18 17:09:13.002 10094-10094/com.lu.aop E/PerformanceAop: method MyApplication.onCreate() cost:28
AOP はより洗練された方法で実装されており、既存のコードにまったく影響を与えず、簡単に変更できます。
時間のかかるタスクを非同期で実行する
アプリケーション内でこれらのサードパーティ製ライブラリの初期化を実行すると、アプリケーション全体の起動処理が遅くなるため、サブスレッドとメインスレッドを並行して使用してメインスレッドの作業を分担し、実行速度を軽減します。メインスレッドの時間。
子スレッドでタスクを実行する
次の 2 つの方法が容易に考えられます。
- 方法 1
public void onCreate(){
new Thread() {
public run() {
//执行任务1
//执行任务2
//执行任务3
}
}.start();
}
- 方法 2
public void onCreate(){
new Thread() {
public run() {
//执行任务1
}
}.start();
new Thread() {
public run() {
//执行任务2
}
}.start();
new Thread() {
public run() {
//执行任务3
}
}.start();
}
2 番目の方法は CPU リソースを最大限に活用しますが、スレッドを直接作成できるほど洗練されていないため、スレッド プールを使用してこれらのスレッドを管理することをお勧めします。
スレッドプールの管理
対応するスレッドプールは取得されますが、スレッド数を自由に埋められるわけではないので、それを使いこなす必要があるので、その設定方法をCPU 资源
参考にしてください。AsyncTask
核心线程数
Executors service = Executors.newFixedThreadPool(核心线程个数);
コアスレッド番号の設定を理解するには、AsyncTask ソースコードを参照してください。
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//CORE_POOL_SIZE 就是核心线程数
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
このようにして、コアスレッドの数を設定できます
//参考AsyncTask来设置线程的个数。
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
MyApplication で非同期タスクの実行を実装します。
@Override
public void onCreate() {
super.onCreate();
//参考AsyncTask来设置线程的个数。
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
service.submit(new Runnable() {
@Override
public void run() {
initSQLite();
}
});
service.submit(new Runnable() {
@Override
public void run() {
initImageLoader();
}
});
}
异步加载的代码执行结果
2019-08-18 19:09:38.022 13948-13948/com.lu.aop E/PerformanceAop: method MyApplication.attachBaseContext(..) cost:1
2019-08-18 19:09:38.062 13948-13948/com.lu.aop E/PerformanceAop: method MyApplication.onCreate() cost:4
2019-08-18 19:09:38.078 13948-13967/com.lu.aop E/PerformanceAop: method MyApplication.initSQLite() cost:9
2019-08-18 19:09:38.094 13948-13968/com.lu.aop E/PerformanceAop: method MyApplication.initImageLoader() cost:15
Log ログ データの比較から、メイン スレッドによって実行される onCreate メソッドの実行時間が、元の 28 ミリ秒から 4 ミリ秒に短縮されていることがわかります。したがって、時間のかかるタスクを実行するためにサブスレッドを使用することは非常に良いことです。しかし、ここには別の問題があります。つまり、MainActivity で使用する必要があるため、Application onCreate の実行が完了する前に初期化する必要があるメソッドがいくつかあります。その場合、上記の非同期で問題が発生するため、どのように解決するかです。この問題 毛織物?
非同期タスクは特定の段階で実行する必要がある
initImageLoader() を例にとると、アプリケーション内のサブスレッドがいつ初期化タスクを完了するかはわかりませんが、この時点では MainActivity に入っており、ImageLoader が使用されます。初期化前に ImageLoader を使用することはできません。 Application 内で完了するため、ImageLoader を制御する必要があります。 Application onCreate の実行が完了する前に、初期化が完了します。次に、CountDownLatch を使用する必要があります。
CountDownLatch は、java.util.concurrent パッケージにある同期ツール クラスで、他のスレッドの一連の操作が完了するまで 1 つ以上のスレッドを待機できるようにします。
- アプリケーションで CountDownLatch を定義する
//Application
private CountDownLatch countDownLatch = new CountDownLatch(1);
- initImageLoaderメソッド実行時にcountDownLatch.countDown()を実行する
private void initImageLoader() {
Fresco.initialize(this);
//try {
//模拟耗时
//Thread.sleep(3000);
//} catch (Exception e) {
// e.printStackTrace();
//}
//Log.e(TAG, "初始化initImageLoader完毕");
//数量减一
countDownLatch.countDown();
}
- countDownLatch.await() を待つ
onCreate メソッドの最後で待機します。ここより前に countDownLatch.countDown() が呼び出された場合は直接スキップし、そうでない場合はここで待機します。
public void onCreate() {
super.onCreate();
//参考AsyncTask来设置线程的个数。
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
service.submit(new Runnable() {
@Override
public void run() {
initSQLite();
}
});
service.submit(new Runnable() {
@Override
public void run() {
initImageLoader();
}
});
//在 onCreate 方法中等待,如果在此处之前之前调用了countDownLatch.countDown(),那么就直接跳过,否则就在此等待。
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "Application onCreate 执行完毕");
}
このようにして、アプリケーションの onCreate メソッドは、onCreate メソッドのライフサイクルを終了する前に、非同期タスク initImageLoader が完了するのを待ちます。
要約する
- タスク実行時間の計算について
- AOP アスペクト指向プログラミングの知識を理解する
- AsyncTask のコアスレッド番号とアプリケーションを理解する
- データ初期化時の非同期最適化スキームを学習しました
パフォーマンスの最適化を包括的かつ明確に理解できるように、関連するコア ノート (基礎となるロジックに戻る) を用意しました。https://qr18.cn/FVlo89
パフォーマンスの最適化に関するコアノート:https://qr18.cn/FVlo89
起動の最適化
メモリの最適化
UIの
最適化 ネットワークの最適化
ビットマップの最適化と画像圧縮の最適化:マルチスレッド同時実行の最適化とデータ伝送効率の最適化ボリュームパッケージの最適化https://qr18.cn/FVlo89
「Android パフォーマンス監視フレームワーク」:https://qr18.cn/FVlo89
「Androidフレームワーク学習マニュアル」:https://qr18.cn/AQpN4J
- ブート初期化プロセス
- 起動時に Zygote プロセスを開始する
- 起動時に SystemServer プロセスを開始する
- バインダードライバー
- AMSの起動プロセス
- PMSの起動プロセス
- ランチャーの起動プロセス
- Android の 4 つの主要コンポーネント
- Androidシステムサービス - 入力イベントの配信処理
- Android の基盤となるレンダリング画面更新メカニズムのソース コード分析
- Android のソースコード解析の実践