随着业务发展的不断壮大,同时在迭代的过程中也会慢慢接入许多二方、三方的库,在程序刚开始启动的时候,无论是业务要求还是技术要求,我们往往有一堆需要在App启动,也就是Application里要初始化或者需要预加载的库、数据,随着长时间版本迭代这些相关代码没有任何统一的梳理控制,随意在主线程调用,随意起个线程加载,这就带来了一个问题,冷启动的时候,主线程跑个小2-3s或一坨后台线程竞争cpu资源,导致App打开慢,体现在点击桌面图标后长时间没有反应,或者闪屏停留半天才进入到App首页,从而影响App用户体验。这时候我们往往会收到用户反馈,特别是中低端机。进而引发我们要开始做三件事情了,首先通过工具+埋点的方式找到造成启动的慢的原因,其次采用各种手段把造成启动的慢代码异步,延迟化,最后搭建一套专属于统计自己App启动性能上报机制,监控优化后线上效果和今后业务迭代对启动性能的影响
工具+埋点查找启动慢的原因
Android 提供了两种工具来帮助开发者查看方法调用耗时和一个时间段内cpu各个核执行状况
TraceView
两种方式,第一种是植入代码,个人觉得针对性也强,也不考验手速
Debug.startMethodTracing("custom");
startTrace();
Debug.stopMethodTracing();
注意:这两行代码要在同一个线程成对出现
第二种是通过Android Studio的Monitor的CPU模块生成
这里主要关注第一种,第一种在执行到植入的trace代码后,会生成trace文件,如下:
其中Total Time是某个方法执行总时间,可以理解为方法大括号之间的代码,Self Time是某个方法自身的执行时间,也就是排除调用其它方法的时间,Children Time是某个方法调用的其它方法的耗时,所以Total Time = Self Time + Children Time
另外还有一个非常需要注意的是Wall Clock Time 和Thread Time,其中Wall Clock Time 是一段代码在某个线程上实际执行的时间,但由于cpu是分时间片给线程的,同时一段代码可能由于IO,还是类似调用wait等线程调用方法,阻塞不执行了,此时cpu会分配给其它线程,但这段代码其实并没有执行完,可能等某个条件触发后,轮到这段代码在的线程分到cpu后,继续执行,而执行这段代码cpu真正的用时是Thread Time,所以一般情况Thread Time是小于Wall Clock Time,而我们优化方向应该是降低Thread Time,同时提升cpu的利用率
一般由于TraceView会抓去整个所有线程状态,且对方法调用有性能影响,所以影响判断方法耗时的准确性,所以基本快被弃用了
SysTrace
systrace相比于traceview 轻量级,开销小,可以直接看到cpu利用率,快速发现cpu利用率低
python systrace.py -t 10 [other-options] [categories]
python systrace.py-t 10 -a <package_name> -o xxtrace.html app sched gfx view am wm dalvik binder_driver freq idle load sync
TraceCompat.beginSection("Tag");
.....
.....
.....
TraceCompat.endSection();
主要看Alerts,用绿色
框架圆圈表示在16.6毫秒内渲染以保持每秒60帧稳定所需的帧。渲染时间超过16.6毫秒的帧用黄色
或红色
框架圆圈表示。
如何自定义统计App首次冷启动和启动阶段各个方法的耗时
上面介绍的那些工具呢,基本只能说是在线下Debug用,可其实Android碎片化严重,机器硬件好坏差别大,线下Debug统计终究不能代表真实,并且线下看似优化时间少了,到线上指不定还什么样,所以我们需要一种能统计线上各个关键流程,于是,大部分互联网公司都会在App代码里埋下统计性能用的log并上报生成相关关键流程时间统计。先以App启动时间统计为例。
个人认为定义一个App冷启动的耗时,应该是用户按下桌面图标,到App首页展示这段时间的耗时,而这段时间有一部分是Android Framework里的,比如fork 进程,创建ActivityThread bindApplication等,所以我们开发人员写的代码,最早调用时机是在Application的attachBaseContext里,所以一般从这记录一个开始时间,但是从哪儿算结束时间是一个比较讲究的了,这里拿一个电商APP举例
比方这个淘宝App,如果统计它的启动耗时,用什么时机算作最后时间点表示这时App对于用户来说可见了呢,答案是用首页第一帧的绘制作为最后统计结束时间点,而首页第一帧到底是什么时候呢,有人会说是Activity的onWindowFocusChanged可以作为第一帧,但这是个误区,这个是Activity的首帧,但这个时候界面不一定对用户来说是有意义的可见,因为界面是数据构成的,比如淘宝的首页是各个栏目+商品构建的,所以用Activity的首帧统计的话,这时栏目或商品不一定显示出来,其实对用户没意义。既然首页是栏目+商品的list,那么我们可以用list的第一项的首帧作为最后统计结束时间点,个人感觉这样就再好不过了,那么我们怎么统计呢,可以利用View的一个功能:
view.getViewTreeObserver().addOnPreDrawListener()
通过这个api,我们可以监控到ViewRootImpl在进行performTravels时,在draw之前会回调这个接口,这样我就可以监控到列表第一项的首帧,从而在这里边打点启动结束的统计时间。
在完成启动时间统计打点后,我们只是知道了整个启动流程的总耗时,可通过这个我也只能知道线上App的平均总耗时,可启动过程中,哪个方法在总耗时里占的时间比较多,每个启动的核心流程方法都耗时多少,这个我们还是不知道,这样对App上线后,开展后续优化,也不是特别有利,所以除了启动总耗时外,我们还需要对启动过程中,核心流程,无论是业务初始化、预加载的代码,还是一些基础库的初始化,例如push、网络、multidex、热补丁等等这些都要做统计。可是我们怎么统计呢,难道每个方法调用开始加一个时间点,结束算个差值么,如果初始化的东西少,还easy,拿要是几十个或甚至大厂那种百个的呢,岂不敲死了,况且也不优雅,每次改个埋点,到处找,所以必须找个优雅的埋点方式,这时一个技术帮上我们了,面向切面编程(AOP),在Android Java上我们可以用AspectJ来实现,这里稍微介绍一下,AspectJ,总而言之其利用gradle的apt,在编译阶段生成了一坨代码来完成在方法调用前,方法调用后,或者调用前后等等插入一些切面(大白话就是方法)
- JoinPoint(连接点):表示在程序中明确定义的点,例如,典型的方法调用,对类成员的访问以及异常处理程序块的执行等等,这些都是JoinPoints。连接点是应用程序提供给切面插入的地方在插入地建立AspectJ程序与源程序的连接。
- PointCut(切点):表示一组JoinPoints,这些JoinPoint或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的Advice 将要发生的地方。
- Advice(通知):定义了在 PointCut里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个JoinPoint之前、之后还是代替执行的代码。
利用AspectJ,我们就可以在方法前后加入切面统计某个方法的执行耗时了。(PS:这里补充一下,其实实际过程中,至少我参与过的大厂APP,都是自己实现一套AOP简化版的实现,不会用AspectJ,我自己认为的原因可能是这种动态化生成一坨方法可能与大厂的热修复框架有冲突,至少会导致有些别切面的方法无法修复了,纯属于个人猜测,同时可能虽然AspectJ是动态生成代码不是反射,但可能生成的代码对性能也有影响,所以大厂也不用。)说了这么多,最重要的是我们要用AOP这种思想,实现一套打点统计方法运行时执行时间,而不要到处统计一个方法就在那个方法前后加上log。
主要的启动优化手段
异步化启动代码
这个是我们的第一直觉,把所有耗时的相关初始化代码抛到子线程执行,最好主UI线程啥都不执行,这样不就是最快了么,道理是这么简单,但是真是直接new Thread()或者开个线程池,把各种业务初始化,基础库初始化一抛到后台线程执行就完事了么?那么请思考一下下面的问题。
- 开几个线程初始化?难道有多少初始化任务就开几个么?
- 有些基础库初始化或者某几个业务初始化之间存在依赖怎么办,比如B需要等A初始化好之后才能初始化
- 有的库里边有Handler,但库的作者比较low,没在构造函数里传Looper.getMainLooper()咋办?
- 难道允许各个业务开发人员随便在Application里,或者哪儿的新写新的异步初始化代码?还是大家一起改Application初始化逻辑,谁的慢,谁去改的提前些?
- 有些基础库或业务逻辑虽然异步初始化,但它又必须要保证在进入首屏前初始化完,怎么保证?
基于上边这些可能不完全的遇到的问题,我们打算引用一种叫做pipeline流水线的机制,彻底掌控整个APP异步、同步各种初始化逻辑,解决好各个初始化代码之间的相互依赖、同时保证可受监控等等。在pipeline的机制里,一个pipeline代表原来其中的一个初始化逻辑,我们把pipeline提交到一个pipelineManager里负责执行这些pipeline,同时可以在pipeline里声明我是在异步线程执行,还是同步线程,以及我依赖哪些pipeline,还有优先级之类,最后在真正执行所有pipeline初始化之前会用有向无环图的拓扑排序算法构建一个图,表示一个APP完整的所有初始化流程,触发初始化开始,下面直接上核心代码
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Vector;
/**
* 有向无环图的拓扑排序算法
*/
public class Graph {
//顶点数
private int mVerticeCount;
//邻接表
private List<Integer>[] mAdj;
public Graph(int verticeCount) {
this.mVerticeCount = verticeCount;
mAdj = new ArrayList[mVerticeCount];
for (int i = 0; i < mVerticeCount; i++) {
mAdj[i] = new ArrayList<Integer>();
}
}
/**
* 添加边
*
* @param u from
* @param v to
*/
public void addEdge(int u, int v) {
mAdj[u].add(v);
}
/**
* 拓扑排序
*/
public Vector<Integer> topologicalSort() {
int indegree[] = new int[mVerticeCount];
for (int i = 0; i < mVerticeCount; i++) {//初始化所有点的入度数量
ArrayList<Integer> temp = (ArrayList<Integer>) mAdj[i];
for (int node : temp) {
indegree[node]++;
}
}
Queue<Integer> queue = new LinkedList<Integer>();
for (int i = 0; i < mVerticeCount; i++) {//找出所有入度为0的点
if (indegree[i] == 0) {
queue.add(i);
}
}
int cnt = 0;
Vector<Integer> topOrder = new Vector<Integer>();
while (!queue.isEmpty()) {
int u = queue.poll();
topOrder.add(u);
for (int node : mAdj[u]) {//找到该点(入度为0)的所有邻接点
if (--indegree[node] == 0) {//把这个点的入度减一,如果入度变成了0,那么添加到入度0的队列里
queue.add(node);
}
}
cnt++;
}
if (cnt != mVerticeCount) {//检查是否有环,理论上拿出来的点的次数和点的数量应该一致,如果不一致,说明有环
throw new IllegalStateException("Exists a cycle in the graph");
}
return topOrder;
}
}
import android.support.annotation.NonNull;
import android.support.v4.util.ArraySet;
import com.optimize.performance.launchstarter.task.Task;
import com.optimize.performance.launchstarter.utils.DispatcherLog;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class PipelineSortUtil {
private static List<Pipeline> sPipelinesHigh = new ArrayList<>();// 高优先级的Task
/**
* 任务的有向无环图的拓扑排序
*
* @return
*/
public static synchronized List<Pipeline> getSortResult(List<Pipeline> originTasks,
List<Class<? extends Pipeline>> clsLaunchPipelines) {
long makeTime = System.currentTimeMillis();
Set<Integer> dependSet = new ArraySet<>();
Graph graph = new Graph(originPipelines.size());
for (int i = 0; i < originPipelines.size(); i++) {
Pipeline pipeline = originPipelines.get(i);
if (pipeline.isSend() || pipeline.dependsOn() == null || pipeline.dependsOn().size() == 0) {
continue;
}
for (Class cls : pipeline.dependsOn()) {
int indexOfDepend = getIndexOfTask(originPipelines, clsLaunchPipelines, cls);
if (indexOfDepend < 0) {
throw new IllegalStateException(pipeline.getClass().getSimpleName() +
" depends on " + cls.getSimpleName() + " can not be found in task list ");
}
dependSet.add(indexOfDepend);
graph.addEdge(indexOfDepend, i);
}
}
List<Integer> indexList = graph.topologicalSort();
List<Pipeline> newTasksAll = getResultTasks(originPipelines, dependSet, indexList);
DispatcherLog.i("task analyse cost makeTime " + (System.currentTimeMillis() - makeTime));
printAllTaskName(newPipelinesAll);
return newPipelinesAll;
}
@NonNull
private static List<Pipeline> getResultTasks(List<Pipeline> originTasks,
Set<Integer> dependSet, List<Integer> indexList) {
List<Pipeline> newTasksAll = new ArrayList<>(originTasks.size());
List<Pipeline> newTasksDepended = new ArrayList<>();// 被别人依赖的
List<Pipeline> newTasksWithOutDepend = new ArrayList<>();// 没有依赖的
List<Pipeline> newTasksRunAsSoon = new ArrayList<>();// 需要提升自己优先级的,先执行(这个先是相对于没有依赖的先)
for (int index : indexList) {
if (dependSet.contains(index)) {
newPipelinesDepended.add(originPipelines.get(index));
} else {
Task task = originTasks.get(index);
if (task.needRunAsSoon()) {
newTasksRunAsSoon.add(task);
} else {
newTasksWithOutDepend.add(task);
}
}
}
// 顺序:被别人依赖的————》需要提升自己优先级的————》需要被等待的————》没有依赖的
sNewPipelinesHigh.addAll(newPipelinesDepended);
sNewPipelinesHigh.addAll(newPipelinesRunAsSoon);
newPipelinesAll.addAll(sNewPipelinesHigh);
newPipelinesAll.addAll(newPipelinesWithOutDepend);
return newPipelinesAll;
}
private static void printAllPipelineName(List<Pipeline> newPipelinesAll) {
if (true) {
return;
}
for (Pipeline pipeline : newPipelinesAll) {
DispatcherLog.i(pipeline.getClass().getSimpleName());
}
}
public static List<Pipeline> getTasksHigh() {
return sNewPipelinesHigh;
}
/**
* 获取任务在任务列表中的index
*
* @param originTasks
* @param taskName
* @return
*/
private static int getIndexOfTask(List<Pipeline> originTasks,
List<Class<? extends Pipeline>> clsLaunchPipelines, Class cls) {
int index = clsLaunchPipelines.indexOf(cls);
if (index >= 0) {
return index;
}
// 仅仅是保护性代码
final int size = originPipelines.size();
for (int i = 0; i < size; i++) {
if (cls.getSimpleName().equals(originPipelines.get(i).getClass().getSimpleName())) {
return i;
}
}
return index;
}
}
import android.os.Process;
import android.support.annotation.IntRange;
import java.util.List;
import java.util.concurrent.Executor;
public interface IPipeline {
/**
* 优先级的范围,可根据Task重要程度及工作量指定;之后根据实际情况决定是否有必要放更大
*
* @return
*/
@IntRange(from = Process.THREAD_PRIORITY_FOREGROUND, to = Process.THREAD_PRIORITY_LOWEST)
int priority();
void run();
/**
* Task执行所在的线程池,可指定,一般默认
*
* @return
*/
Executor runOn();
/**
* 依赖关系
*
* @return
*/
List<Class<? extends Task>> dependsOn();
/**
* 异步线程执行的Task是否需要在被调用await的时候等待,默认不需要
*
* @return
*/
boolean needWait();
/**
* 是否在主线程执行
*
* @return
*/
boolean runOnMainThread();
/**
* 只是在主进程执行
*
* @return
*/
boolean onlyInMainProcess();
/**
* Task主任务执行完成之后需要执行的任务
*
* @return
*/
Runnable getTailRunnable();
void setPipelineCallBack(PipelineCallBack callBack);
boolean needCall();
}
/**
* Pipeline调用类
*/
public class PipelineManager {
private long mStartTime;
private static final int WAITTIME = 10000;
private static Context sContext;
private static boolean sIsMainProcess;
private List<Future> mFutures = new ArrayList<>();
private static volatile boolean sHasInit;
private List<Pipeline> mAllPipelines = new ArrayList<>();
private List<Class<? extends Pipeline>> mClsAllPipelines = new ArrayList<>();
private volatile List<Task> mMainThreadPipelines = new ArrayList<>();
private CountDownLatch mCountDownLatch;
private AtomicInteger mNeedWaitCount = new AtomicInteger();//保存需要Wait的Task的数量
private List<Pipeline> mNeedWaitTasks = new ArrayList<>();//调用了await的时候还没结束的且需要等待的Task
private volatile List<Class<? extends Pipeline>> mFinishedTasks = new ArrayList<>(100);//已经结束了的Task
private HashMap<Class<? extends Pipeline>, ArrayList<Pipeline>> mDependedHashMap = new HashMap<>();
private AtomicInteger mAnalyseCount = new AtomicInteger();//启动器分析的次数,统计下分析的耗时;
private PipelineManager() {
}
public static void init(Context context) {
if (context != null) {
sContext = context;
sHasInit = true;
sIsMainProcess = Utils.isMainProcess(sContext);
}
}
/**
* 注意:每次获取的都是新对象
*
* @return
*/
public static PipelineManager createInstance() {
if (!sHasInit) {
throw new RuntimeException("must call TaskDispatcher.init first");
}
return new TaskDispatcher();
}
public PipelineManager addTask(Pipeline pipeline) {
if (pipeline != null) {
collectDepends(pipeline);
mAllTasks.add(pipeline);
mClsAllTasks.add(pipeline.getClass());
// 非主线程且需要wait的,主线程不需要CountDownLatch也是同步的
if (ifNeedWait(pipeline)) {
mNeedWaitTasks.add(pipeline);
mNeedWaitCount.getAndIncrement();
}
}
return this;
}
private void collectDepends(Pipeline pipeline) {
if (pipeline.dependsOn() != null && task.dependsOn().size() > 0) {
for (Class<? extends Task> cls : pipeline.dependsOn()) {
if (mDependedHashMap.get(cls) == null) {
mDependedHashMap.put(cls, new ArrayList<Pipeline>());
}
mDependedHashMap.get(cls).add(pipeline);
if (mFinishedTasks.contains(cls)) {
pipeline.satisfy();
}
}
}
}
private boolean ifNeedWait(Pipeline pipeline) {
return !pipeline.runOnMainThread() && pipeline.needWait();
}
@UiThread
public void start() {
mStartTime = System.currentTimeMillis();
if (Looper.getMainLooper() != Looper.myLooper()) {
throw new RuntimeException("must be called from UiThread");
}
if (mAllPipelines.size() > 0) {
mAnalyseCount.getAndIncrement();
printDependedMsg();
mAllPipelines = TaskSortUtil.getSortResult(mAllPipelines, mClsAllPipelines);
mCountDownLatch = new CountDownLatch(mNeedWaitCount.get());
sendAndExecuteAsyncPipelines();
DispatcherLog.i("task analyse cost " + (System.currentTimeMillis() - mStartTime) + " begin main ");
executePipelineMain();
}
DispatcherLog.i("task analyse cost startTime cost " + (System.currentTimeMillis() - mStartTime));
}
public void cancel() {
for (Future future : mFutures) {
future.cancel(true);
}
}
private void executePipelineMain() {
mStartTime = System.currentTimeMillis();
for (Pipeline pipeline : mMainThreadPipelines) {
long time = System.currentTimeMillis();
new DispatchRunnable(pipeline,this).run();
DispatcherLog.i("real main " + pipeline.getClass().getSimpleName() + " cost " +
(System.currentTimeMillis() - time));
}
DispatcherLog.i("maintask cost " + (System.currentTimeMillis() - mStartTime));
}
private void sendAndExecuteAsyncTasks() {
for (Pipeline pipeline : mAllPipelines) {
if (pipeline.onlyInMainProcess() && !sIsMainProcess) {
markTaskDone(pipeline);
} else {
sendTaskReal(pipeline);
}
pipeline.setSend(true);
}
}
/**
* 查看被依赖的信息
*/
private void printDependedMsg() {
DispatcherLog.i("needWait size : " + (mNeedWaitCount.get()));
if (false) {
for (Class<? extends Task> cls : mDependedHashMap.keySet()) {
DispatcherLog.i("cls " + cls.getSimpleName() + " " + mDependedHashMap.get(cls).size());
for (Pipeline pipeline : mDependedHashMap.get(cls)) {
DispatcherLog.i("cls " + task.getClass().getSimpleName());
}
}
}
}
/**
* 通知Children一个前置任务已完成
*
* @param launchTask
*/
public void satisfyChildren(Pipeline launchPipeline) {
ArrayList<Pipeline> arrayList = mDependedHashMap.get(launchPipeline.getClass());
if (arrayList != null && arrayList.size() > 0) {
for (Pipeline pipeline : arrayList) {
pipeline.satisfy();
}
}
}
public void markTaskDone(Pipeline pipeline) {
if (ifNeedWait(pipeline)) {
mFinishedPipelines.add(pipeline.getClass());
mNeedWaitPipelines.remove(pipeline);
mCountDownLatch.countDown();
mNeedWaitCount.getAndDecrement();
}
}
private void sendTaskReal(final Pipeline pipeline) {
if (pipeline.runOnMainThread()) {
mMainThreadTasks.add(pipeline);
if (pipeline.needCall()) {
pipeline.setTaskCallBack(new PipelineCallBack() {
@Override
public void call() {
PipelineStat.markTaskDone();
pipeline.setFinished(true);
satisfyChildren(pipeline);
markTaskDone(pipeline);
DispatcherLog.i(pipeline.getClass().getSimpleName() + " finish");
Log.i("testLog", "call");
}
});
}
} else {
// 直接发,是否执行取决于具体线程池
Future future = pipeline.runOn().submit(new DispatchRunnable(pipeline,this));
mFutures.add(future);
}
}
public void executeTask(Pipeline pipeline) {
if (ifNeedWait(pipeline)) {
mNeedWaitCount.getAndIncrement();
}
pipeline.runOn().execute(new DispatchRunnable(pipeline,this));
}
@UiThread
public void await() {
try {
if (DispatcherLog.isDebug()) {
DispatcherLog.i("still has " + mNeedWaitCount.get());
for (Pipeline pipeline : mNeedWaitPipelines) {
DispatcherLog.i("needWait: " + pipeline.getClass().getSimpleName());
}
}
if (mNeedWaitCount.get() > 0) {
mCountDownLatch.await(WAITTIME, TimeUnit.MILLISECONDS);
}
} catch (InterruptedException e) {
}
}
public static Context getContext() {
return sContext;
}
public static boolean isMainProcess() {
return sIsMainProcess;
}
}
以上就是Pipeline核心机制的
延迟初始化
有些初始化的任务的产物可能不是App启动后马上必须的,或者说是为了性能更好做的预加载,但是如果我们只是在Application里简单粗暴的用handler postDelay 好几秒,或者在首屏打开之后handler postDelay,又或者更稍微注意点,在上边说的首页list第一项第一帧回调后handler postDelay,来触发延迟初始化,这样都会带来一个问题,就是可能你触发后某个时间点执行,这时用户正在和界面产生UI交互,这时做这些,会可能给占用主线程一些时间,导致在一帧里做了好多事,ui渲染跨帧了,引起卡顿,不好的用户体验,那么我们应该在什么时候触发这么不是那么重要的延迟初始化呢,说白了,应该在用户不操作App ui那些瞬间,或者称为App闲置的时候,可我们怎么知道App闲置了呢,Android为我们提供了非常好的回调,就是IdelHandler,这里引用腾讯一篇介绍IdelHandler的好文章
你知道android的MessageQueue.IdleHandler吗? - 腾讯WeTestwetest.qq.comimport android.os.Looper;
import android.os.MessageQueue;
import com.optimize.performance.launchstarter.task.DispatchRunnable;
import com.optimize.performance.launchstarter.task.Task;
import java.util.LinkedList;
import java.util.Queue;
public class DelayInitDispatcher {
private Queue<Task> mDelayTasks = new LinkedList<>();
private MessageQueue.IdleHandler mIdleHandler = new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
if(mDelayTasks.size()>0){
Task task = mDelayTasks.poll();
new DispatchRunnable(task).run();
}
return !mDelayTasks.isEmpty();
}
};
public DelayInitDispatcher addTask(Task task){
mDelayTasks.add(task);
return this;
}
public void start(){
Looper.myQueue().addIdleHandler(mIdleHandler);
}
}
通过以上Pipeline机制和IdelHandler我们就可以大幅度提升App 初始化的性能以及初始化代码的优雅度了,下面我们再看看有没有其它好的加快App性能的地方。
其它
- attachBaseContext提前加载 SharedPerference,触发读取SharedPerference的xml到内存
- 预加载一些加载耗时的类
- cpu锁频
- 进来启动时不开子进程
- 启动时停止GC
- 类似Redex的解决方法