「性能优化1.0」启动分类及启动时间的测量
「性能优化1.1」计算方法的执行时间
「性能优化1.2」异步优化
一、异步优化
在上一小节中,我通过获取应用的启动时间和每一个方法执行之间之后,我们发现,如果在 Application 或者 MainActivity 生命周期中串行去执行这些第三方库的初始化,是会拖慢整个应用的启动过程的,因此我们想通过子线程与主线程并行的方式来分担主线程的工作,从而减少主线程的执行时间。
1.1、让任务执行在子线程中
1.1.1、常规方案
我们常规的方式是怎样的呢?
public void onCreate(){
new Thread() {
public run() {
//执行任务1
//执行任务2
//执行任务3
}
}.start();
}
但是这样是不优雅的,首先直接 new Thread()
这种方式比较简单粗暴,而且这里只是开启一个线程,我们最初的想法是想每一个异步任务就使用一个线程去执行。那么我们的伪代码就变成如下这种方式:
public void onCreate(){
new Thread() {
public run() {
//执行任务1
}
}.start();
new Thread() {
public run() {
//执行任务2
}
}.start();
new Thread() {
public run() {
//执行任务3
}
}.start();
}
那要多个线程,那我就创建多个线程呗,当然这种方式确实要比第一种好一些,因为它可以更加充分地利用 CPU ,但是直接创建线程还是不优雅,所以使用线程池来管理这些线程会好一些。
1.1.2、线程池管理
通过以下方式就可以获取到我们对应的线程池,但是这个线程个数不能随意填,我们要能充分利用到 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));
因此有了这个核心线程数
之后我们的代码就变成如下方式:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
//参考AsyncTask来设置线程的个数。
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
关于
核心线程数
的设置是一个比较小的知识点,不过这个对 CPU 资源的利用是很有帮助的。
通过改造后,我们现在的代码如下:
@Override
public void onCreate() {
super.onCreate();
//参考AsyncTask来设置线程的个数。
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
service.submit(new Runnable() {
@Override
public void run() {
initBugly();
}
});
service.submit(new Runnable() {
@Override
public void run() {
initImageLoader();
}
});
}
还记得我们来上一小节中使用 AOP 来计算每一个方法的耗时,那么现在我们来对比一下通过异步加载和没有异步加载这两种方式的时间差别。
- 没有异步加载的代码执行结果
2019-03-17 20:29:12.946 10094-10094/com.example.perfermance E/PerformanceAop: method MyApplication.attachBaseContext(..) cost:1
2019-03-17 20:29:12.979 10094-10094/com.example.perfermance E/PerformanceAop: method MyApplication.initBugly() cost:12
2019-03-17 20:29:13.002 10094-10094/com.example.perfermance E/PerformanceAop: method MyApplication.initImageLoader() cost:23
2019-03-17 20:29:13.002 10094-10094/com.example.perfermance E/PerformanceAop: method MyApplication.onCreate() cost:35
- 异步加载的代码执行结果:
2019-03-17 22:07:38.022 13948-13948/com.example.perfermance E/PerformanceAop: method MyApplication.attachBaseContext(..) cost:1
2019-03-17 22:07:38.062 13948-13948/com.example.perfermance E/PerformanceAop: method MyApplication.onCreate() cost:3
2019-03-17 22:07:38.078 13948-13967/com.example.perfermance E/PerformanceAop: method MyApplication.initBugly() cost:15
2019-03-17 22:07:38.094 13948-13968/com.example.perfermance E/PerformanceAop: method MyApplication.initImageLoader() cost:28
通过两次输出的 log 数据对比,可以看出主线程执行的 onCreate
方法的执行时间从原来的 35ms 减到到了 3ms 。
但是这里又有另外一个问题,那就是有一些方法是必须在 Application onCreate 执行完成之前完成初始化的,因为在 MainActivity 中就需要使用到,那我们上面的异步就会有问题了,那如何解决这个问题呢?
1.1.3、异步任务必须在某一个阶段执行完成
我们还是以 initBugly()
方法来举例,这个方法是在异步线程中执行,如何控制让其在 Application onCreate 执行完毕之前它先完成呢?
这时就需要使用到 CountDownLatch
了,我们先来看看示例图:
- 定义一个
CountDownLatch
//Application
private CountDownLatch countDownLatch = new CountDownLatch(1);
- 在方法执行完毕时,执行
countDownLatch.countDown()
private void initBugly() {
try {
//模拟initBugly耗时
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
Log.e(TAG, "初始化initBugly完毕");
//数量减一
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() {
initBugly();
}
});
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 执行完毕");
}
这样,我们的 Application onCreate 方法就会等待异步任务 initBugly 执行完毕之后才会结束 onCreate 这个方法的生命周期。