Android性能优化—-发动优化
在了解了发动优化后,Application会做一些初始化的作业,但不要在Application中做耗时操作,然而有些初始化作业可能是很耗时的,那怎么办?初始化操作能够开启子线程来完结。
核算履行时刻
- 常规计划(手动埋点符号)
- 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 的办法来高雅的获取Application每一个办法的履行时刻。
2.1引进 AspectJ
在 Android 中经过 AspectJ 这个库来运用 AOP ,接下来来引进这个库:
- 在工程根 build.gradle 中引进 AspectJ 插件
- classpath ‘com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4’
- 在Module中 build.gradle 使用插件
- apply plugin: ‘android-aspectjx’
- 在Module中build.gradle 中引进 aspectj 库
- implementation ‘org.aspectj:aspectjrt:1.8.9’
2.2AOP的具体运用
- 界说一个类PerformanceAop运用@Aspect注解
- @Around(“execution(* com.lu.aop.MyApplication.**(..))”)表示需要对 MyApplication中的每一个办法都做 hook 处理。
- 记载办法履行前后的时刻戳,并核算对应的时刻差。
AOP具体运用
@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 这种办法是一个比较高雅的办法完结的,它对已有代码是零侵入性的,修正也便利。
用异步履行耗时使命
在 Application 去履行这些第三方库的初始化,是会拖慢整个使用的发动过程的,因而用子线程与主线程并行的办法来分担主线程的作业,然后减少主线程的履行时刻。
在子线程中履行使命
咱们可能会简单想到以下这两种办法:
- 办法一
public void onCreate(){
new Thread() {
public run() {
//履行使命1
//履行使命2
//履行使命3
}
}.start();
}
- 办法二
public void onCreate(){
new Thread() {
public run() {
//履行使命1
}
}.start();
new Thread() {
public run() {
//履行使命2
}
}.start();
new Thread() {
public run() {
//履行使命3
}
}.start();
}
办法二愈加充分地利用 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 办法的履行时刻从原来的 28ms 减到到了 4ms 。所以用子线程履行耗时使命还是相当好的。可是到这里又遇到一个问题,便是有一些办法是必须在 Application onCreate 履行完结之前完结初始化的,因为在 MainActivity 中就需要运用到,那咱们上面的异步就会有问题了,那怎么处理这个问题呢?
异步使命必须在某一个阶段履行完结
以initImageLoader()为例,不知道Application中的子线程什么时分才完结初始化使命,可是这个时分现已进入了MainActivity了,用到ImageLoader,ImageLoader在Application中还没有完结初始化就用不了,所以得控制ImageLoader在 Application onCreate 履行完结之前完结初始化。这时就需要运用到 CountDownLatch 了。
CountDownLatch是一种java.util.concurrent包下一个同步工具类,它答应一个或多个线程等候直到在其他线程中一组操作履行完结。
- 在Application中界说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 履行完毕");
}
这样,咱们的 Application onCreate 办法就会等候异步使命 initImageLoader 履行完毕之后才会完毕 onCreate 这个办法的生命周期。
总结
- 了解核算履行使命时刻
- 了解AOP面向切面编程知识
- 了解AsyncTask的中心线程数及运用
- 学习了初始化数据时异步优化计划