一個應用App的啓動速度可以影響用戶的首次體驗,啓動速度較慢(感官上)的應用可能致使用戶再次開啓App的意圖降低,或者卸載放棄該應用程序。本文會經過如下幾個方面來介紹應用啓動的相關指標和優化,提供應用的啓動速度。html
總體文章思路以下:python
一般來講,啓動方式分爲兩種:冷啓動和熱啓動。android
二者之間的特色以下:程序員
冷熱啓動時間的計算命令:面試
adb shell am start -W [packageName]/[packageName.XxxActivity]
能夠看到二者時間相差比較大。
根據該命令基本能夠看出一個應用的啓動速度了,從冷啓動熱啓動的相關關係,當咱們須要優化啓動速度的時候,優化冷啓動速度便可。
可是該命令咱們只是大概知道應用的啓動速度,但並不知道咱們的應用具體哪一個位置耗時,影響啓動速度,後續我會介紹如何獲取啓動具體耗時時間。shell
常規獲取時間方法無非就是在方法執行前記錄下時間,在方法執行完畢後記錄時間,二者時間之差就是該方法執行的時間,封裝一個基礎類以下:編程
public class LaunchTimer { private static final String TAG = "LaunchTimer"; private static long sTime; public static void startRecord() { sTime = System.currentTimeMillis(); } public static void endRecord() { long cost = System.currentTimeMillis() - sTime; NLog.i(TAG, "執行耗時:%s", cost); } }
使用方式以下,能夠直觀的看出createController方法執行的時間微信
這樣已經很直觀了,能夠具體到該方法的執行時間,若是要繼續分析則對該方法內部繼續執行該代碼便可。可是這裏有一個問題若是要知道10個或者更多方法的執行時間,這個方法看起來是能夠,但寫起來過於繁瑣,且不符合程序員的習慣,關於這種場景後面會介紹如何處理。架構
@Override public void onCreate(final Bundle icicle) { setTheme(R.style.BrowserTheme); Intent intent = getIntent(); NLog.i(LOGTAG,"onCreate"); super.onCreate(icicle); //開始記錄,且該方法能夠設置文件大小和路徑 Debug.startMethodTracing("browser.trace"); Controller controller=createController(); mController = controller; getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); controller.handleThirdPartyIntent(intent); //結束記錄 Debug.stopMethodTracing(); }
如上能夠在目錄下能夠生成以下文件併發
/sdcard/Android/data/com.xxx.xx.browser/files/browser.trace
導出改文件,經過Android Studio的profile打開改文件
1處能夠看出有多少線程。
2處能夠看出具體方法的耗時。
3處有兩個選項:
requestPermission(); ExecutorService service = Executors.newSingleThreadExecutor(new NamedThreadFactory("Controller")); Future<Boolean> future = service.submit(new Callable<Boolean>() { @Override public Boolean call() throws Exception { try { asyncInit(); } catch (Exception e) { return false; } return true; } });
requestPermission()方法執行在main線程中,所以咱們能夠把其放在Controller0線程中執行,從而減小main線程的的時間
ExecutorService service = Executors.newSingleThreadExecutor(new NamedThreadFactory("Controller")); Future<Boolean> future = service.submit(new Callable<Boolean>() { @Override public Boolean call() throws Exception { try { requestPermission(); asyncInit(); } catch (Exception e) { return false; } return true; } });
經測試發現無問題,且對比此時的trace文件發現修改先後main線程時間相對來講減小不少。
在代碼的開始位置加上tag
TraceCompat.beginSection("AppOnCreate");
而後指定位置結束
TraceCompat.endSection();
便可以抓取到整個應用在此過程的相關信息,例如在onCreate方法中添加上述兩行代碼,執行相關python命令:
python systrace.py -b 32768 -t 10 -a com.xxx.xxx.browser -o browser.html sched gfx view wm am app
操做相關應用,便可以抓取整個過程的相關信息:
便可以看到添加的tag「AppOnCreate」,對應的時間信息:
AspectJ其實是對AOP編程思想的一個實踐,固然,除了AspectJ之外,還有不少其它的AOP實現,例如ASMDex,但目前最好、最方便的,依然是AspectJ。
AspectJ的使用以下:
根目錄gradle下引用:
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
app目錄gradle文件下引用:
implementation 'org.aspectj:aspectjrt:1.8.+'
此兩處引用完成以後,就是代碼編寫:
package com.xx.xxx.browser.aspect; import android.util.Log; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @Aspect public class IntercepLifeCycleAOP { //獲取該Activity下的全部on開頭的方法耗時 @Around("execution(* com.xxx.xxx.BrowserActivity.on**(..))") public Object getTime(ProceedingJoinPoint joinPoint) { Object proceed = null; long start = System.currentTimeMillis(); try { proceed = joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } long end = System.currentTimeMillis(); Log.d("IntercepLifeCycleAOP", joinPoint.getSignature().getName() + ":執行時間:" + (end - start)); return proceed; } }
引入以後結果以下:
能夠看到具體方法的耗時。
採用註解方式,其中Around 須要有必定的AspectJ相關的語法
1.採用線程加載一些資源,好比sdk初始化,配置信息拉取等相關資源。線程,線程池,IntentServices都可以,配合延遲效果更好。
2.當咱們採用線程之間的可能會存在各線程之間相互等待依賴等相關問題,資源A線程必須在資源B加載完成,才能加載,但二者又會在不一樣的線程之間,此時簡單的辦法能夠採用CountDownLatch來實現。其總體思路以下圖
3.使用 Pipeline 機制,根據業務優先級規定業務初始化時機,制定啓動框架,它們爲各個任務創建依賴關係,最終構成一個有向無環圖。對於能夠併發的任務,會經過線程池最大程度提高啓動速度。不管是微信的mmkernel 仍是阿里的Alpha 都具有這種能力。
4.其餘方案:
除了上述幾種,咱們也能夠利用IdealHandler,dex分包等相關方式作到啓動優化。
上面主要介紹瞭如何獲取啓動的相關事件和相關優化知識點。關於時間就是儘可能使用工具,關於優化總體思路就是能預加載能延遲加載的資源儘可能去預加載去延遲加載,能異步的業務儘可能異步。
固然優化這個話題也是要根據具體的業務邏輯來定,總之:
對於啓動優化要警戒 KPI 化,咱們要解決的不是一個數字,而是用戶真正的體驗問題。
上述只是提供一些思路和方式,還有不少奇淫技巧,歡迎給位大佬評論指出。
感謝你們能耐着性子看完囉裏囉嗦的文章
在這裏我也分享一份私貨,本身收錄整理的Android學習PDF+架構視頻+面試文檔+源碼筆記,還有高級架構技術進階腦圖、Android開發面試專題資料,高級進階架構資料幫助你們學習提高進階,也節省你們在網上搜索資料的時間來學習,也能夠分享給身邊好友一塊兒學習
若是你有須要的話,能夠點贊+評論,關注我,而後加我VX:15388039515 我發給你
(或關注微信公衆號「Android開發之家」回覆【資料】免費領取)