冷啓動優化-轉摘https://www.jianshu.com/p/d97b390da87b

探究Android的冷啓動優化

96 
BlackSwift 
2016.04.02 03:40* 字數 1464 閱讀 4118評論 6

Cold Startup Perfermance Improvement in Androidcss


本文依據平臺以下html

  • 機型: 魅藍Note(高通615真八核/2G/1080P/4.4)
  • 效果:1.1s -> 0.7s(實際用戶看到的假界面時間更短)
  • 檢測網站: https://nimbledroid.com,是 @程序亦非猿 推薦的哦

1. 啓動過程概述

在應用層,普通APP啓動過程大體以下:java

  1. 加載Application
    • 靜態代碼段/構造函數
    • onCreate方法
  2. 加載主Activity
    • 靜態代碼段/構造函數
    • 消息隊列第一次循環: onCreate,經過setContentview解析、加載xml
    • 消息隊列第二次循環: 被動地調用Choreographerd中的FrameDisplayEventReceiver的run()進行進行實際繪製

爲了提升用戶感知,我但願在主線程中執行的順序以下(注意本流程不適用於插件化的App):android

  1. 儘快顯示DecoView(Main Thread)(顯示Theme中定義的ActionBar、背景等)
  2. 儘快顯示xml中的靜態View(Main Thread)(顯示xml中的佈局)
  3. 加載第三方黑盒SDK(Main Thread)
  4. 進行網絡、圖片等框架的構造(Main Thread)
  5. 經過框架進行業務請求(Gson/OkHttp等, Worker Thread),並更新View

不建議在Application中初始化耗時任務,它將直接致使白屏git

2. 用戶感知優化

本部分能夠提升上文1,2,3的用戶體驗github

2.1. 加載僞背景(0.1~0.2s)

DecoView的優先級比setContentView優先級更高,因此可讓DecoView顯示一個僞啓動背景界面,而不是白屏黑屏或者沒界面甩鍋給手機廠商,讓用戶感覺到App正在加載是一個好的選擇。面試

繪製一個App啓動的草圖,以下,一個是Toolbar,一個是背景緩存

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque" > <item android:gravity="top"> <shape android:shape="rectangle"> <solid android:color="#c8ececec"/> </shape> </item> <item android:top="75dp" android:gravity="top"> <shape android:shape="rectangle"> <solid android:color="@color/primary"/> </shape> </item> </layer-list> 

設置windowBackground安全

<style name="ColdStartTheme" parent="APPTheme"> <item name="android:windowBackground">@drawable/cold_start_bg</item> </style> 

在啓動時先加載了僞背景,而後才加載了真正的View元素bash

 
僞背景加載 → 繪製完成View

最終可讓用戶以爲「提升」了0.1~0.2s的速度

參考文章:

  1. avoding-android-cold-starts
  2. Android冷啓動時間優化 - Wayne's blog
  3. GitHub - MaterialColdStart

上述方案均不能很好處理狀態欄,若是你使用Translucent,慎用

2.2. XML佈局優化

此部分適用於解析、處理、繪製靜態xml時的優化

xml佈局優化是老生常談的話題了,本質是減小無謂的繪製,網上面試寶典不少,這裏就也不介紹了。解決方法以下:

  1. 使用Include,Merge,viewStub簡化佈局
  2. 使用相對佈局,layer-list下降樹的層級
  3. 使用gone標籤能夠跳過繪製
  4. 被遮擋的view避免重複繪製

參考文章:

  1. http://stormzhang.com/android/2014/04/10/android-optimize-layout/
  2. http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0125/2356.html

3. 延後啓動耗時框架

本部分不能壓縮總時間,只是將耗時操做移動到後面而已,可讓白屏時間減小0.2~0.3s(取決於框架數量)。

3.1. 實現方法

在onCreate()的最後,加入post操做,便可實如今繪製XmlView完成後再進行非UI的耗時操做

getWindow().getDecorView().post(new Runnable() { @Override public void run() { //加載Applicaiton中的框架 40+ms GlobalContext.startThirdFrameWork(); //構建網絡框架 120ms repo = SquareUtils.getRetrofit(URL).create(GithubService.class); //進行ssl庫的初始化請求 40+ms onRefresh(); } }); 

3.2. 實現原理

在XML被inflate後,須要經過mDecoView.addView(xmlView)進行添加。

addview最終調用ViewRootImpl的方法scheduleTraversals(),進行了消息隊列的優先獨佔操做

mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

接着調用doTraversal()釋放

mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); 

SyncBarrier擁有消息隊列的獨佔性,當使用SyncBarrier時,後面的消息將被阻塞,這樣在主線程中就有更多的CPU時間能夠分給WMS進行繪圖了。在View繪製完成後,解除SyncBarrier後纔會調用咱們在上文Post的耗時框架加載任務,這樣就實現了延遲加載。

4. 多線程初始化

此部分真的能夠壓縮啓動時間,可是對SDK線程安全有必定的要求,在黑盒SDK下容易出現問題

下文複用了OkHttp中的單例Worker線程池,節省了0.16s的啓動時間

SquareUtils.getDispatcher().executorService().execute(new Runnable() { @Override public void run() { Log.d(TAG, "run: " + System.currentTimeMillis()); //42ms GlobalContext.startThirdFrameWork(); //120ms repo = SquareUtils.getRetrofit(DanbooruAPI.KONACHAN).create(DanbooruAPI.class); runOnUiThread(new Runnable() { @Override public void run() { //40ms onRefresh(); } }); } }); 

最後,你就能比較充分利用你的真八核手機

主線程: 解析xml ----------addView()--------| → 更新界面
線程池: 初始化框架 --post(請求網絡)---wait()--| 

5. 混淆

通過測試,混淆在必定程度上能夠提升速度,屬於免費的性能提高,可是不是很是明顯,大概只有100ms

混淆後要記得測試

6. 總結

經過上述方法,能夠壓榨0.3~0.6s的時間,讓用戶可以更快的啓動APP

本文例子: Github - AnimeWallpaper,目前啓動速度0.7s,求各位star!

附錄. Retrofit框架加載時間分析

Retrofit 在知乎上有人這樣回答的,大意是動態代理 == 反射 == 慢,這就是典型的半桶水,不懂裝懂。

經過對每一個方法進行統計後,結果倒是這樣的:

retrofit構造(128ms)

  • 構造OkHttp:121ms, 其中javax.ssl構建耗時117ms,調用的是一個SSL遍歷native操做,這個基本沒法避免;緩存文件初始化1ms
  • 構造GsonFactory 4ms: 主要是classloader加載的時間
  • 其餘 3ms

retrofit訪問網絡前接口的拼裝(42ms)

  • RxJava框架: 12ms
  • 動態代理: 1ms
  • Gson庫: 27ms,主要進行反射操做
  • 其餘: 2ms

隨着SSL的普及,javax.ssl必然會被加載,這個100ms的時間在native中黑盒執行,很難避免,只能等手機ROM去優化嘍;剩下的就是Gson的時間比較久,這個時間仍是能夠接受的。

從上面也能夠看出,與動態代理相關的時間,並無想象中那麼慢,不要看到反射就以爲慢,網絡I/O請求與以後拼裝的時間加起來,比動態代理要多的多

相關文章
相關標籤/搜索