写在前面
赶着学赶着写,实不实用就完事了!
这里的优化我们针对的是冷启动时的优化,有关冷启动等相关概念可以看这篇文章:APP启动方式分析——冷启动、热启动、温启动
而冷启动的第二阶段,创建app对象、启动主进程、创建MainActivity、渲染视图、执行onLayout、执行onDraw,这部分是我们能够真正控制的时间,即Application和Activity生命周期中进行的操作。
目录
启动时间测量
对启动进行优化,首先我们要学会如何去测量启动时间
1.adb命令
adb shell am start -W packagename/首屏Activity
adb命令有问题的可以看一下这篇文章:adb问题
运行后是这个样子,我们会得到三个数据,ThisTime、TotalTime和WaitTime
ThisTime:最后一个Activity启动耗时
TotalTime:所有Activity启动耗时
WaitTime:AMS启动Activity的总耗时
这种方法测量,在线下使用方便,但是不能带到线上运行,且测量的时间不是精确时间
2.手动埋点
原理:启动时埋点,启动结束埋点,计算二者差值
误区:onWindowFocusChanged只是首帧时间
正确:真实数据展示,Feed第一条展示
这种方法测量的时间精确,且可带到线上
traceview
traceview可以通过图形的形式展示执行时间、调用栈等,信息全面,包含所有线程
使用方式:
Debug.startMethodTracing(getFilesDir() + "/App.trace");
// code...
Debug.stopMethodTracing(getFilesDir() + "/App.trace");
这里最好手动为其设置trace文件输出路径,如果出现问题找不到trace文件可以看一下这篇文章:解决使用Debug.startMethodTracing后找不到对应的.trace文件
打开trace文件:
这里的调用关系是上面的函数调用了下面的函数,而对应的颜色:
橙色:系统
绿色:应用自身的函数调用
蓝色:第三方
可以通过右键->jump to source看对应代码
traceview测量的运行时间不是准确的运行时间,因为traceview运行时开销严重,整体都会变慢:会抓取当前运行的所有线程的所有执行函数。如果过度依赖traceview的时间分析,可能会带偏优化方向。
Theme切换
Theme切换是针对启动时的白屏问题,因为此时是系统运行时间,不可控,所以这里的优化不是真正的缩短运行时间,而是给人感觉启动时间快。
具体操作方式:给首屏Activity添加一个style,背景自定义,这样开屏就会显示出想要的样子;首屏Activity的onCreate中,在setContentView前将style改回。
异步优化
此处的异步优化针对的是Application在启动时需要初始化的n多任务,此处占用了App启动的很多时间。
核心思想:子线程分担主线程任务,并行减少时间;将线性的多个任务,改为多线程并行,缩短时间
自定义启动器
核心思想:充分利用CPU多核的能力;自动梳理任务顺序
1)代码Task化,启动逻辑抽象为Task
2)根据所有任务依赖关系排序生成一个有向无环图
3)多线程按照排序后的优先级依次执行
这里要注意:
1)初始化是否符合异步要求
2)Task之间的先后依赖关系
3)区分CPU密集型和IO密集型
代码实现
代码连接:LaunchOptimizeManager.java
首先最开始列出所有的任务,为其定义唯一的名称,String[] allTasks
用来统计所有的Task(后面要将未执行的Task筛选掉,防止出现我注释掉了一个Task,导致需要他先执行的后面的Task无法执行的情况出现)。我们是通过拓扑排序来决定每个Task的执行顺序的,void topologicalSort(List<LaunchTask> tasks)
中的逻辑分为一下几步:
1.使用多线程的线程池去执行Task:无前置任务的Task才能够执行,即能够执行的Task之间不会相互依赖,使用多线程执行可减少执行时间;线程池事先封装好,方便管理
2.队列+boolean[],轮询所有Task
3.每个Task的实际执行就是一个Runnable,再次封装增加完成的回调
4.AtomicInteger记录完成数量,作为循环跳出条件
5.增加TIME_OUT防卡死
/**
* @author Johnny Deng
* @version 1.0
* @description 优化启动
* @date 2020/6/25 17:50
*/
public class LaunchOptimizeManager {
private static final int TIME_OUT = 5000;
public static final String TASK_LEAKCANARY = "task_leakcanary";
public static final String TASK_BMOB = "task_bmob";
public static final String TASK_BUGLY = "task_bugly";
public static final String TASK_ANDFIX = "task_andfix";
private static String[] allTasks = new String[] {
TASK_LEAKCANARY, TASK_BMOB, TASK_BUGLY, TASK_ANDFIX};
/**
* 拓扑排序执行启动项
*
* @param tasks tasks
*/
public static void topologicalSort(List<LaunchTask> tasks) {
if (tasks == null || tasks.size() == 0) {
return;
}
// 执行时间
long startTime = System.currentTimeMillis();
// 统计当前完成的数量
AtomicInteger count = new AtomicInteger();
ExecutorService executorService = ThreadUtils.getCachedThreadPool();
Set<String> set = new HashSet<>();
LinkedList<LaunchTask> queue = new LinkedList<>();
// 任务执行后,刷新排序图
Callback callback = name -> {
synchronized (tasks) {
count.getAndIncrement();
for (LaunchTask task : tasks) {
if (task.pres.size() > 0) {
task.pres.remove(name);
if (task.pres.size() == 0) {
queue.add(task);
tasks.remove(task);
}
}
}
}
};
for (LaunchTask task : tasks) {
set.add(task.name);
task.setCallback(callback);
}
// 排除未执行的Task
if (set.size() < allTasks.length) {
for (String sT : allTasks) {
if (!set.contains(sT)) {
for (LaunchTask task : tasks) {
task.pres.remove(sT);
}
}
}
}
// TODO 环
// 将可以执行的任务加入队列中
for (int i = tasks.size() - 1; i >= 0; i --) {
LaunchTask task = tasks.get(i);
if (task.pres.isEmpty()) {
queue.add(task);
tasks.remove(task);
}
}
while (!queue.isEmpty() || !tasks.isEmpty() || count.get() < tasks.size()) {
long cur = System.currentTimeMillis();
if (cur - startTime > TIME_OUT) {
LogUtils.e("App Launch up Time out!");
break;
}
if (queue.isEmpty()) {
continue;
}
LaunchTask task = queue.poll();
executorService.submit(task);
}
}
public static class LaunchTask implements Runnable {
private Runnable runnable;
private List<String> pres;
private String name;
private Callback callback;
public LaunchTask(Runnable runnable, List<String> pres, String name) {
this.runnable = runnable;
this.pres = pres;
this.name = name;
}
public void setCallback(Callback callback) {
this.callback = callback;
}
@Override
public void run() {
runnable.run();
if (callback != null) {
callback.onComplete(name);
}
}
}
public interface Callback {
void onComplete(String name);
}
}
最后贴一下项目地址:jio-deng/FFmpegInAndroid
持续更新~