随着業務發展的不斷壯大,同時在疊代的過程中也會慢慢接入許多二方、三方的庫,在程式剛開始啟動的時候,無論是業務要求還是技術要求,我們往往有一堆需要在App啟動,也就是Application裡要初始化或者需要預加載的庫、資料,随着長時間版本疊代這些相關代碼沒有任何統一的梳理控制,随意在主線程調用,随意起個線程加載,這就帶來了一個問題,冷啟動的時候,主線程跑個小2-3s或一坨背景線程競争cpu資源,導緻App打開慢,展現在點選桌面圖示後長時間沒有反應,或者閃屏停留半天才進入到App首頁,進而影響App使用者體驗。這時候我們往往會收到使用者回報,特别是中低端機。進而引發我們要開始做三件事情了,首先通過工具+埋點的方式找到造成啟動的慢的原因,其次采用各種手段把造成啟動的慢代碼異步,延遲化,最後搭建一套專屬于統計自己App啟動性能上報機制,監控優化後線上效果和今後業務疊代對啟動性能的影響
工具+埋點查找啟動慢的原因
Android 提供了兩種工具來幫助開發者檢視方法調用耗時和一個時間段内cpu各個核執行狀況
TraceView
兩種方式,第一種是植入代碼,個人覺得針對性也強,也不考驗手速
Debug.startMethodTracing("custom");
startTrace();
Debug.stopMethodTracing();
注意:這兩行代碼要在同一個線程成對出現
第二種是通過Android Studio的Monitor的CPU子產品生成
這裡主要關注第一種,第一種在執行到植入的trace代碼後,會生成trace檔案,如下:
traceview
其中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.com
import 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
支付寶用戶端架構解析:Android 用戶端啟動速度優化之「垃圾回收」yq.aliyun.com
- 類似Redex的解決方法
支付寶 App 建構優化解析:通過安裝包重排布優化 Android 端啟動性能yq.aliyun.com