今日頭條APP啟動(dòng)很快衬吆,原來(lái)是做了這些優(yōu)化梁钾?
[終端研發(fā)部](javascript:void(0);) 今天
點(diǎn)擊上方的終端研發(fā)部,右上角選擇“設(shè)為星標(biāo)”
每日早8點(diǎn)半逊抡,技術(shù)文章準(zhǔn)時(shí)送上
公眾號(hào)后臺(tái)回復(fù)“學(xué)習(xí)”姆泻,獲取作者獨(dú)家秘制精品資料
往期文章
這個(gè)View有點(diǎn)酷零酪,填空題View特效走起!
剛?cè)胄胁痪媚床绾螌?xiě)出優(yōu)雅的Java代碼四苇?
作者:奶蓋ww
前言
網(wǎng)上關(guān)于啟動(dòng)優(yōu)化的文章多不勝數(shù)方咆,內(nèi)容千篇一律月腋,大都是列舉一些耗時(shí)操作,采用異步加載瓣赂、懶加載等榆骚。
而在面試過(guò)程中,關(guān)于啟動(dòng)優(yōu)化的問(wèn)題煌集,如果只是很表面地回答耗時(shí)操作應(yīng)該放在子線程妓肢,顯然太過(guò)于普通,無(wú)法跟競(jìng)爭(zhēng)者拉開(kāi)差距苫纤。如何讓面試官知道你的“內(nèi)功深厚”碉钠,那肯定是要往原理層面去回答。本文重點(diǎn)還是關(guān)注原理卷拘,冷啟動(dòng)優(yōu)化這個(gè)問(wèn)題能延伸到很多原理層面的知識(shí)點(diǎn)放钦,本文比較有意思的地方是通過(guò)反編譯今日頭條App,研究大廠的啟動(dòng)優(yōu)化方案恭金。講啟動(dòng)優(yōu)化之前操禀,先看下應(yīng)用的啟動(dòng)流程
一、應(yīng)用啟動(dòng)流程
應(yīng)用進(jìn)程不存在的情況下横腿,從點(diǎn)擊桌面應(yīng)用圖標(biāo)颓屑,到應(yīng)用啟動(dòng)(冷啟動(dòng)),大概會(huì)經(jīng)歷以下流程:
- Launcher startActivity
- AMS startActivity
- Zygote fork 進(jìn)程
- ActivityThread main()
4.1. ActivityThread attach
4.2. handleBindApplication
4.3 attachBaseContext
4.4. installContentProviders
4.5. Application onCreate - ActivityThread 進(jìn)入loop循環(huán)
- Activity生命周期回調(diào)耿焊,onCreate揪惦、onStart、onResume…
整個(gè)啟動(dòng)流程我們能干預(yù)的主要是 4.3罗侯、4.5 和6器腋,應(yīng)用啟動(dòng)優(yōu)化主要從這三個(gè)地方入手。理想狀況下钩杰,這三個(gè)地方如果不做任何耗時(shí)操作纫塌,那么應(yīng)用啟動(dòng)速度就是最快的,但是現(xiàn)實(shí)很骨感讲弄,很多開(kāi)源庫(kù)接入第一步一般都是在Application onCreate方法初始化措左,有的甚至直接內(nèi)置ContentProvider,直接在ContentProvider中初始化框架避除,不給你優(yōu)化的機(jī)會(huì)怎披。
二胸嘁、啟動(dòng)優(yōu)化
直奔主題,常見(jiàn)的啟動(dòng)優(yōu)化方式大概有這些:
- 閃屏頁(yè)優(yōu)化
- MultipDex優(yōu)化(本文重點(diǎn))
- 第三方庫(kù)懶加載
- WebView優(yōu)化
- 線程優(yōu)化
- 系統(tǒng)調(diào)用優(yōu)化
2.1 閃屏頁(yè)優(yōu)化
消除啟動(dòng)時(shí)的白屏/黑屏凉逛,市面上大部分App都采用了這種方法性宏,非常簡(jiǎn)單,是一個(gè)障眼法状飞,不會(huì)縮短實(shí)際冷啟動(dòng)時(shí)間毫胜,簡(jiǎn)單貼下實(shí)現(xiàn)方式吧。這樣打開(kāi)桌面圖標(biāo)會(huì)馬上顯示logo菩佑,不會(huì)出現(xiàn)黑/白屏自晰,直到Activity啟動(dòng)完成,替換主題稍坯,logo消失酬荞,但是總的啟動(dòng)時(shí)間并沒(méi)有改變。
2.2 MultiDex 優(yōu)化(本文重點(diǎn))
說(shuō)MultiDex之前瞧哟,先梳理下apk編譯流程
2.2.1 apk編譯流程
Android Studio 按下編譯按鈕后發(fā)生了什么混巧?
- 打包資源文件,生成R.java文件(使用工具AAPT)
- 處理AIDL文件勤揩,生成java代碼(沒(méi)有AIDL則忽略)
- 編譯 java 文件咧党,生成對(duì)應(yīng).class文件(java compiler)
- .class 文件轉(zhuǎn)換成dex文件(dex)
- 打包成沒(méi)有簽名的apk(使用工具apkbuilder)
- 使用簽名工具給apk簽名(使用工具Jarsigner)
- 對(duì)簽名后的.apk文件進(jìn)行對(duì)齊處理,不進(jìn)行對(duì)齊處理不能發(fā)布到Google Market(使用工具zipalign)
在第4步陨亡,將class文件轉(zhuǎn)換成dex文件傍衡,默認(rèn)只會(huì)生成一個(gè)dex文件,單個(gè)dex文件中的方法數(shù)不能超過(guò)65536负蠕,不然編譯會(huì)報(bào)錯(cuò):
Unable to execute dex: method ID not in [0, 0xffff]: 65536
App集成一堆庫(kù)之后蛙埂,方法數(shù)一般都是超過(guò)65536的,解決辦法就是:一個(gè)dex裝不下遮糖,用多個(gè)dex來(lái)裝绣的,gradle增加一行配置即可。
multiDexEnabled true
這樣解決了編譯問(wèn)題欲账,在5.0以上手機(jī)運(yùn)行正常屡江,但是5.0以下手機(jī)運(yùn)行直接crash,報(bào)錯(cuò) Class NotFound xxx赛不。Android 5.0以下盼理,ClassLoader加載類的時(shí)候只會(huì)從class.dex(主dex)里加載,ClassLoader不認(rèn)識(shí)其它的class2.dex俄删、class3.dex宏怔、…奏路,當(dāng)訪問(wèn)到不在主dex中的類的時(shí)候,就會(huì)報(bào)錯(cuò):Class NotFound xxx臊诊,因此谷歌給出兼容方案鸽粉,MultiDex。
2.2.2 MultiDex 原來(lái)這么耗時(shí)
在Android 4.4的機(jī)器打印MultiDex.install(context)耗時(shí)如下:
平均耗時(shí)1秒以上抓艳,目前大部分應(yīng)用應(yīng)該還是會(huì)兼容5.0以下手機(jī)触机,那么MultiDex優(yōu)化是冷啟動(dòng)優(yōu)化的大頭。為什么MultiDex會(huì)這么耗時(shí)玷或?老規(guī)矩儡首,分析一下MultiDex原理~
2.2.3 MultiDex 原理
下面看下MultiDex的install 方法做了什么事
從入口的判斷來(lái)看,如果虛擬機(jī)本身就支持加載多個(gè)dex文件偏友,那就啥都不用做蔬胯;如果是不支持加載多個(gè)dex(5.0以下是不支持的),則走到 doInstallation 方法位他。
先看注釋1氛濒,MultiDexExtractor#load
查找dex文件,有兩個(gè)邏輯鹅髓,有緩存就調(diào)用loadExistingExtractions方法舞竿,沒(méi)有緩存或者緩存讀取失敗就調(diào)用performExtractions方法,然后再緩存起來(lái)窿冯。使用到緩存骗奖,那么performExtractions 方法想必應(yīng)該是很耗時(shí)的,分析一下代碼:
這里的邏輯就是解壓apk醒串,遍歷出里面的dex文件重归,例如class1.dex,class2.dex厦凤,然后又壓縮成class1.zip鼻吮,class2.zip…,然后返回zip文件列表较鼓。****思考為什么這里要壓縮呢椎木?后面涉及到ClassLoader加載類原理的時(shí)候會(huì)分析ClassLoader支持的文件格式。第一次加載才會(huì)執(zhí)行解壓和壓縮過(guò)程博烂,第二次進(jìn)來(lái)讀取sp中保存的dex信息香椎,直接返回file list,所以第一次啟動(dòng)的時(shí)候比較耗時(shí)禽篱。dex文件列表找到了畜伐,回到上面MultiDex#doInstallation方法的注釋2,找到的dex文件列表躺率,然后調(diào)用installSecondaryDexes方法進(jìn)行安裝玛界,怎么安裝呢万矾?方法點(diǎn)進(jìn)去看SDK 19 以上的實(shí)現(xiàn)
- 反射ClassLoader 的 pathList 字段
- 找到pathList 字段對(duì)應(yīng)的類的makeDexElements 方法
- 通過(guò)MultiDex.expandFieldArray 這個(gè)方法擴(kuò)展 dexElements 數(shù)組,怎么擴(kuò)展慎框?看下代碼:
就是創(chuàng)建一個(gè)新的數(shù)組良狈,把原來(lái)數(shù)組內(nèi)容(主dex)和要增加的內(nèi)容(dex2、dex3…)拷貝進(jìn)去,反射替換原來(lái)的dexElements為新的數(shù)組,如下圖
看起來(lái)有點(diǎn)眼熟为牍,Tinker熱修復(fù)的原理也是通過(guò)反射將修復(fù)后的dex添加到這個(gè)dex數(shù)組去,不同的是熱修復(fù)是添加到數(shù)組最前面严嗜,而MultiDex是添加到數(shù)組后面。這樣講可能還不是很好理解洲敢?來(lái)看看ClassLoader怎么加載一個(gè)類的就明白了~
2.2.4 ClassLoader 加載類原理
不管是 PathClassLoader還是DexClassLoader漫玄,都繼承自BaseDexClassLoader,加載類的代碼在 BaseDexClassLoader中4.4 源碼/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
- 構(gòu)造方法通過(guò)傳入dex路徑沦疾,創(chuàng)建了DexPathList称近。
- ClassLoader的findClass方法最終是調(diào)用DexPathList 的findClass方法
接著看DexPathList源碼 /dalvik/src/main/java/dalvik/system/DexPathList.java
DexPathList里面定義了一個(gè)dexElements 數(shù)組第队,findClass方法中用到哮塞,看下
findClass方法邏輯很簡(jiǎn)單,就是遍歷dexElements 數(shù)組凳谦,拿到里面的DexFile對(duì)象忆畅,通過(guò)DexFile的loadClassBinaryName方法加載一個(gè)類。
最終創(chuàng)建Class是通過(guò)native方法尸执,就不追下去了家凯,大家有興趣可以看下native層是怎么創(chuàng)建Class對(duì)象的。DexFile.cpp那么問(wèn)題來(lái)了如失,5.0以下這個(gè)dexElements 里面只有主dex(可以認(rèn)為是一個(gè)bug)绊诲,沒(méi)有dex2、dex3…褪贵,MultiDex是怎么把dex2添加進(jìn)去呢? 答案就是反射DexPathList的dexElements字段掂之,然后把我們的dex2添加進(jìn)去,當(dāng)然脆丁,dexElements里面放的是Element對(duì)象世舰,我們只有dex2的路徑,必須轉(zhuǎn)換成Element格式才行槽卫,所以反射DexPathList里面的makeDexElements 方法跟压,將dex文件轉(zhuǎn)換成Element對(duì)象即可。
dex2歼培、dex3…通過(guò)makeDexElements方法轉(zhuǎn)換成要新增的Element數(shù)組震蒋,最后一步就是反射DexPathList的dexElements字段茸塞,將原來(lái)的Element數(shù)組和新增的Element數(shù)組合并,然后反射賦值給dexElements變量喷好,最后DexPathList的dexElements變量就包含我們新加的dex在里面了翔横。****makeDexElements方法會(huì)判斷file類型,上面講dex提取的時(shí)候解壓apk得到dex梗搅,然后又將dex壓縮成zip禾唁,壓縮成zip,就會(huì)走到第二個(gè)判斷里去无切。仔細(xì)想想荡短,其實(shí)dex不壓縮成zip,走第一個(gè)判斷也沒(méi)啥問(wèn)題吧哆键,那谷歌的MultiDex為什么要將dex壓縮成zip呢掘托?在Android開(kāi)發(fā)高手課中看到張紹文也提到這一點(diǎn)
然后我在反編譯頭條App的時(shí)候,發(fā)現(xiàn)頭條參考谷歌的MultiDex籍嘹,自己寫(xiě)了一套闪盔,猜想可能是優(yōu)化這個(gè)多余的壓縮過(guò)程,頭條的方案下面會(huì)介紹辱士。
2.2.5 原理小結(jié)
ClassLoader 加載類原理:
ClassLoader.loadClass -> DexPathList.loadClass -> 遍歷dexElements數(shù)組 ->DexFile.loadClassBinaryName
通俗點(diǎn)說(shuō)就是:ClassLoader加載類的時(shí)候是通過(guò)遍歷dex數(shù)組泪掀,從dex文件里面去加載一個(gè)類,加載成功就返回颂碘,加載失敗則拋出Class Not Found 異常异赫。MultiDex原理:
在明白ClassLoader加載類原理之后,我們可以通過(guò)反射dexElements數(shù)組头岔,將新增的dex添加到數(shù)組后面塔拳,這樣就保證ClassLoader加載類的時(shí)候可以從新增的dex中加載到目標(biāo)類,經(jīng)過(guò)分析后最終MultipDex原理圖如下:
2.2.6 MultiDex 優(yōu)化(兩種方案)
知道了MultiDex原理之后峡竣,可以理解install過(guò)程為什么耗時(shí)靠抑,因?yàn)樯婕暗浇鈮篴pk取出dex、壓縮dex适掰、將dex文件通過(guò)反射轉(zhuǎn)換成DexFile對(duì)象颂碧、反射替換數(shù)組。那么MultiDex到底應(yīng)該怎么優(yōu)化呢攻谁,放子線程可行嗎稚伍?
方案1:子線程install(不推薦)
這個(gè)方法大家很容易就能想到,在閃屏頁(yè)開(kāi)一個(gè)子線程去執(zhí)行MultiDex.install戚宦,然后加載完才跳轉(zhuǎn)到主頁(yè)个曙。需要注意的是閃屏頁(yè)的Activity,包括閃屏頁(yè)中引用到的其它類必須在主dex中,不然在MultiDex.install之前加載這些不在主dex中的類會(huì)報(bào)錯(cuò)Class Not Found垦搬。這個(gè)可以通過(guò)gradle配置呼寸,如下:
maindexlist.txt 文件指定哪些類要打包到主dex中,內(nèi)容格式如下
報(bào)錯(cuò)NoClassDefFoundError,一般都是該類沒(méi)有在主dex中栅干,要在maindexlist.txt 將配置指定在主dex迈套。 第三方庫(kù)中的ContentProvider必須指定在主dex中,否則也會(huì)找不到碱鳞,為什么桑李?文章開(kāi)頭說(shuō)過(guò)應(yīng)用的啟動(dòng)流程,ContentProvider 初始化時(shí)機(jī)如下圖:
ContentProvider初始化太早了窿给,如果不在主dex中贵白,還沒(méi)啟動(dòng)閃屏頁(yè)就已經(jīng)crash了。所以這種方案的缺點(diǎn)很明顯:
這時(shí)候就思考一下靴寂,有沒(méi)有其它更好的方案呢?大廠是怎么做的召耘?今日頭條肯定要對(duì)MultiDex進(jìn)行優(yōu)化吧百炬,反編譯瞧瞧?
- MultiDex加載邏輯放在閃屏頁(yè)的話崩泡,閃屏頁(yè)中引用到的類都要配置在主dex禁荒。
- ContentProvider必須在主dex,一些第三方庫(kù)自帶ContentProvider允华,維護(hù)比較麻煩圈浇,要一個(gè)一個(gè)配置寥掐。
點(diǎn)了一根煙之后污它,開(kāi)始偷代碼…
MultiDex優(yōu)化方案2:今日頭條方案
今日頭條沒(méi)有加固剖踊,反編譯后很容易通過(guò)關(guān)鍵字搜索找到MultidexApplication這個(gè)類,
看注釋1的d.a(this);這個(gè)方法衫贬,代碼雖然混淆了德澈,但是方法內(nèi)部的代碼還是可以看出是干嘛的,繼續(xù)跟這個(gè)方法固惯,為了不影響閱讀梆造,我對(duì)混淆做了一些處理,改成正常的方法名葬毫。
每個(gè)方法開(kāi)頭都有PatchProxy.isSupport這個(gè)if判斷镇辉,這個(gè)是美團(tuán)Robust熱修復(fù)生成的代碼屡穗,今日頭條沒(méi)有自己的熱修復(fù)框架,沒(méi)有用Tinker忽肛,而是用美團(tuán)的村砂,想了解關(guān)于Robust細(xì)節(jié)可以參考文末鏈接。Robust直接跳過(guò)屹逛,看else代碼塊即可础废。繼續(xù)看loadMultiDex方法
邏輯如下:
1. 創(chuàng)建臨時(shí)文件,作為判斷MultiDex是否加載完的條件
2. 啟動(dòng)LoadDexActivity去加載MultiDex(LoadDexActivity在單獨(dú)進(jìn)程)罕模,加載完會(huì)刪除臨時(shí)文件
3. 開(kāi)啟while循環(huán)色迂,直到臨時(shí)文件不存在才跳出循環(huán),進(jìn)入Application的onCreate創(chuàng)建臨時(shí)文件代碼
按照上面代碼分析歇僧,今日頭條在5.0以下手機(jī)首次啟動(dòng)應(yīng)該是這樣:
- 打開(kāi)桌面圖標(biāo)
- 顯示默認(rèn)背景
- 跳轉(zhuǎn)到加載dex的界面,展示一個(gè)loading的加載框幾秒鐘
- 跳轉(zhuǎn)到閃屏頁(yè)
效果跟今日頭條是一致的柄错,不再重復(fù)分析代碼了舷夺,源碼上傳到github,感興趣的同學(xué)可以參考參考售貌,頭條的方案给猾,值得嘗試~ github.com/lanshifu/Mu…再次梳理一下這種方式:
- 在主進(jìn)程Application 的 attachBaseContext 方法中判斷如果需要使用MultiDex,則創(chuàng)建一個(gè)臨時(shí)文件颂跨,然后開(kāi)一個(gè)進(jìn)程(LoadDexActivity)敢伸,顯示Loading,異步執(zhí)行MultiDex.install 邏輯恒削,執(zhí)行完就刪除臨時(shí)文件并finish自己池颈。
- 主進(jìn)程Application 的 attachBaseContext 進(jìn)入while代碼塊,定時(shí)輪循臨時(shí)文件是否被刪除钓丰,如果被刪除躯砰,說(shuō)明MultiDex已經(jīng)執(zhí)行完,則跳出循環(huán)携丁,繼續(xù)正常的應(yīng)用啟動(dòng)流程琢歇。
- 注意LoadDexActivity 必須要配置在main dex中。
有些同學(xué)可能會(huì)問(wèn),啟動(dòng)還是很久啊矿微,冷啟動(dòng)時(shí)間有變化嗎痕慢?冷啟動(dòng)時(shí)間是指點(diǎn)擊桌面圖標(biāo)到第一個(gè)Activity顯示這段時(shí)間。
MultiDex優(yōu)化總結(jié)
方案1:直接在閃屏頁(yè)開(kāi)個(gè)子線程去執(zhí)行MultiDex邏輯涌矢,MultiDex不影響冷啟動(dòng)速度掖举,但是難維護(hù)。****方案2:今日頭條的MultiDex優(yōu)化方案:
- 在Application 的attachBaseContext 方法里娜庇,啟動(dòng)另一個(gè)進(jìn)程的LoadDexActivity去異步執(zhí)行MultiDex邏輯塔次,顯示Loading。
- 然后主進(jìn)程Application進(jìn)入while循環(huán)名秀,不斷檢測(cè)MultiDex操作是否完成
- MultiDex執(zhí)行完之后主進(jìn)程Application繼續(xù)走励负,ContentProvider初始化和Application onCreate方法,也就是執(zhí)行主進(jìn)程正常的邏輯匕得。
其實(shí)應(yīng)該還有方案3继榆,因?yàn)槲野l(fā)現(xiàn)頭條并沒(méi)有直接使用Google的MultiDex,而是參考谷歌的MultiDex汁掠,自己寫(xiě)了一套略吨,耗時(shí)應(yīng)該會(huì)少一些,大家有興趣可以去研究一下考阱。
2.3 預(yù)創(chuàng)建Activity
這段代碼是今日頭條里面的翠忠,Activity對(duì)象預(yù)先new出來(lái),
對(duì)象第一次創(chuàng)建的時(shí)候乞榨,java虛擬機(jī)首先檢查類對(duì)應(yīng)的Class 對(duì)象是否已經(jīng)加載秽之。如果沒(méi)有加載,jvm會(huì)根據(jù)類名查找.class文件吃既,將其Class對(duì)象載入考榨。同一個(gè)類第二次new的時(shí)候就不需要加載類對(duì)象,而是直接實(shí)例化态秧,創(chuàng)建時(shí)間就縮短了董虱。
頭條真是把啟動(dòng)優(yōu)化做到極致扼鞋。
2.4 第三方庫(kù)懶加載
很多第三方開(kāi)源庫(kù)都說(shuō)在Application中進(jìn)行初始化申鱼,十幾個(gè)開(kāi)源庫(kù)都放在Application中,肯定對(duì)冷啟動(dòng)會(huì)有影響云头,所以可以考慮按需初始化捐友,例如Glide,可以放在自己封裝的圖片加載類中溃槐,調(diào)用到再初始化匣砖,其它庫(kù)也是同理,讓Application變得更輕。
2.5 WebView啟動(dòng)優(yōu)化猴鲫。
WebView啟動(dòng)優(yōu)化文章也比較多对人,這里只說(shuō)一下大概優(yōu)化思路。
- WebView第一次創(chuàng)建比較耗時(shí)拂共,可以預(yù)先創(chuàng)建WebView牺弄,提前將其內(nèi)核初始化。
- 使用WebView緩存池宜狐,用到WebView的地方都從緩存池取势告,緩存池中沒(méi)有緩存再創(chuàng)建,注意內(nèi)存泄漏問(wèn)題抚恒。
- 本地預(yù)置html和css咱台,WebView創(chuàng)建的時(shí)候先預(yù)加載本地html,之后通過(guò)js腳本填充內(nèi)容部分俭驮。
這一部分可以參考:mp.weixin.qq.com/s/KwvWURD5W…
2.6 數(shù)據(jù)預(yù)加載
這種方式一般是在主頁(yè)空閑的時(shí)候回溺,將其它頁(yè)面的數(shù)據(jù)加載好,保存到內(nèi)存或數(shù)據(jù)庫(kù)混萝,等到打開(kāi)該頁(yè)面的時(shí)候馅而,判斷已經(jīng)預(yù)加載過(guò),直接從內(nèi)存或數(shù)據(jù)庫(kù)讀取數(shù)據(jù)并顯示譬圣。
2.7 線程優(yōu)化
線程是程序運(yùn)行的基本單位瓮恭,線程的頻繁創(chuàng)建是耗性能的,所以大家應(yīng)該都會(huì)用線程池厘熟。單個(gè)cpu情況下屯蹦,即使是開(kāi)多個(gè)線程,同時(shí)也只有一個(gè)線程可以工作绳姨,所以線程池的大小要根據(jù)cpu個(gè)數(shù)來(lái)確定登澜。啟動(dòng)優(yōu)化方式就先介紹到這里,常見(jiàn)的就是這些飘庄,其它的可以作為補(bǔ)充脑蠕。
Android學(xué)習(xí)PDF+架構(gòu)視頻+面試文檔+源碼筆記
三、啟動(dòng)耗時(shí)分析方法
TraceView性能損耗太大跪削,得到的結(jié)果不真實(shí)谴仙。 Systrace 可以方便追蹤關(guān)鍵系統(tǒng)調(diào)用的耗時(shí)情況,如 Choreographer碾盐,但是不支持應(yīng)用程序代碼的耗時(shí)分析晃跺。
3.1 Systrace + 函數(shù)插樁
結(jié)合Systrace 和 函數(shù)插樁,就是將如下代碼插入到每個(gè)方法的入口和出口
插樁工具參考:github.com/AndroidAdva…mac下systrace路徑在
/Users/{xxx}/Library/Android/sdk/platform-tools/systrace/
編譯運(yùn)行app毫玖,執(zhí)行命令
python2
/Users/lanshifu/Library/Android/sdk/platform-tools/systrace/systrace.py gfx view wm am pm ss dalvik app sched -b 90960 -a com.sample.systrace -o test.log.html
最后按下Enter停止捕獲trace信息掀虎,在目錄下生成報(bào)告test.log.html凌盯,直接可以用谷歌瀏覽器打開(kāi)查看。
3.2 BlockCanary 也可以檢測(cè)
BlockCanary 可以監(jiān)聽(tīng)主線程耗時(shí)的方法烹玉,將閾值設(shè)置低一點(diǎn)驰怎,比如200毫秒,這樣的話如果一個(gè)方法執(zhí)行時(shí)間超過(guò)200毫秒二打,獲取堆棧信息并通知開(kāi)發(fā)者砸西。BlockCanary 原理在之前那篇卡頓優(yōu)化的文章里面講過(guò)一些,這里就不再重復(fù)址儒。
總結(jié)
文章有點(diǎn)長(zhǎng)芹枷,看到這里,是不是忘記開(kāi)頭講什么了莲趣?總結(jié)一下這篇文章主要涉及到哪些內(nèi)容:
- 應(yīng)用啟動(dòng)流程
- 閃屏頁(yè)優(yōu)化
- MultiDex 原理分析
- ClassLoader 加載一個(gè)類的流程分析
- 熱修復(fù)原理
- MultiDex優(yōu)化:介紹了兩種方式鸳慈,一種是直接在閃屏頁(yè)開(kāi)個(gè)子線程去加載dex,難維護(hù)喧伞,不推薦走芋;一種是今日頭條的方案,在單獨(dú)一個(gè)進(jìn)程加載dex潘鲫,加載完主進(jìn)程再繼續(xù)翁逞。
- 快速啟動(dòng)Activity的方式:預(yù)創(chuàng)建Activity,預(yù)加載數(shù)據(jù)溉仑。
- 啟動(dòng)時(shí)間監(jiān)控的方式:Systrace+插樁挖函、BlockCanary。
面試問(wèn)到啟動(dòng)優(yōu)化問(wèn)題浊竟,不要簡(jiǎn)單一兩句話回答怨喘,可以說(shuō)說(shuō)自己在實(shí)際項(xiàng)目中做了哪些優(yōu)化,比如Multidex優(yōu)化振定,把整個(gè)流程必怜,原理說(shuō)清楚。當(dāng)然后频,前提是自己要去實(shí)踐梳庆,理解為什么要這樣做。
閱讀更多
IDEA 的優(yōu)雅調(diào)試卑惜,讓 bug 無(wú)處藏身膏执!
相信自己残揉,沒(méi)有做不到的胧后,只有想不到的
在這里獲得的不僅僅是技術(shù)!
喜歡就給個(gè)“在看”
------------------------------轉(zhuǎn)載自 研發(fā)部終端 公眾號(hào) 