今日頭條APP啟動(dòng)很快呆奕,原來(lái)是做了這些優(yōu)化?

今日頭條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ú)家秘制精品資料

image

往期文章

這個(gè)View有點(diǎn)酷零酪,填空題View特效走起!

無(wú)法忘懷的一次百度電話面試

剛?cè)胄胁痪媚床绾螌?xiě)出優(yōu)雅的Java代碼四苇?

不得不裝的21款,Android Studio插件

九月份精選Github項(xiàng)目推薦:xCrash

image

作者:奶蓋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)歷以下流程:

  1. Launcher startActivity
  2. AMS startActivity
  3. Zygote fork 進(jìn)程
  4. ActivityThread main()
    4.1. ActivityThread attach
    4.2. handleBindApplication
    4.3 attachBaseContext
    4.4. installContentProviders
    4.5. Application onCreate
  5. ActivityThread 進(jìn)入loop循環(huán)
  6. 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)方式吧。
image

styles.xml 增加一個(gè)主題叫AppThemeWelcome
image

閃屏頁(yè)設(shè)置這個(gè)主題昔瞧,或者全局給Application設(shè)置
image

這樣的話啟動(dòng)Activity之后背景會(huì)一直在,所以在Activity的onCreate方法中切換成正常主題
image

這樣打開(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ā)生了什么混巧?

  1. 打包資源文件,生成R.java文件(使用工具AAPT)
  2. 處理AIDL文件勤揩,生成java代碼(沒(méi)有AIDL則忽略)
  3. 編譯 java 文件咧党,生成對(duì)應(yīng).class文件(java compiler)
  4. .class 文件轉(zhuǎn)換成dex文件(dex)
  5. 打包成沒(méi)有簽名的apk(使用工具apkbuilder)
  6. 使用簽名工具給apk簽名(使用工具Jarsigner)
  7. 對(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í)如下:

image

平均耗時(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 方法做了什么事

image

從入口的判斷來(lái)看,如果虛擬機(jī)本身就支持加載多個(gè)dex文件偏友,那就啥都不用做蔬胯;如果是不支持加載多個(gè)dex(5.0以下是不支持的),則走到 doInstallation 方法位他。

image

先看注釋1氛濒,MultiDexExtractor#load

image

查找dex文件,有兩個(gè)邏輯鹅髓,有緩存就調(diào)用loadExistingExtractions方法舞竿,沒(méi)有緩存或者緩存讀取失敗就調(diào)用performExtractions方法,然后再緩存起來(lái)窿冯。使用到緩存骗奖,那么performExtractions 方法想必應(yīng)該是很耗時(shí)的,分析一下代碼:

image

這里的邏輯就是解壓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)

image

  1. 反射ClassLoader 的 pathList 字段
  2. 找到pathList 字段對(duì)應(yīng)的類的makeDexElements 方法
  3. 通過(guò)MultiDex.expandFieldArray 這個(gè)方法擴(kuò)展 dexElements 數(shù)組,怎么擴(kuò)展慎框?看下代碼:
image

就是創(chuàng)建一個(gè)新的數(shù)組良狈,把原來(lái)數(shù)組內(nèi)容(主dex)和要增加的內(nèi)容(dex2、dex3…)拷貝進(jìn)去,反射替換原來(lái)的dexElements為新的數(shù)組,如下圖

image

看起來(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,加載類的代碼在 BaseDexClassLoader4.4 源碼/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java

image

  1. 構(gòu)造方法通過(guò)傳入dex路徑沦疾,創(chuàng)建了DexPathList称近。
  2. ClassLoader的findClass方法最終是調(diào)用DexPathList 的findClass方法

接著看DexPathList源碼 /dalvik/src/main/java/dalvik/system/DexPathList.java

image

DexPathList里面定義了一個(gè)dexElements 數(shù)組第队,findClass方法中用到哮塞,看下

image

findClass方法邏輯很簡(jiǎn)單,就是遍歷dexElements 數(shù)組凳谦,拿到里面的DexFile對(duì)象忆畅,通過(guò)DexFile的loadClassBinaryName方法加載一個(gè)類。

image

最終創(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)去呢? 答案就是反射DexPathListdexElements字段掂之,然后把我們的dex2添加進(jìn)去,當(dāng)然脆丁,dexElements里面放的是Element對(duì)象世舰,我們只有dex2的路徑,必須轉(zhuǎn)換成Element格式才行槽卫,所以反射DexPathList里面的makeDexElements 方法跟压,將dex文件轉(zhuǎn)換成Element對(duì)象即可。

image

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)

image

然后我在反編譯頭條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原理圖如下:

image

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配置呼寸,如下:

image

maindexlist.txt 文件指定哪些類要打包到主dex中,內(nèi)容格式如下

image

在已有項(xiàng)目中用這種方式猴贰,一頓操作猛如虎之后对雪,編譯運(yùn)行在4.4的機(jī)器上,啟動(dòng)閃屏頁(yè)米绕,加載完準(zhǔn)備進(jìn)入主頁(yè)直接崩掉了瑟捣。
image

報(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ī)如下圖:

image

ContentProvider初始化太早了窿给,如果不在主dex中贵白,還沒(méi)啟動(dòng)閃屏頁(yè)就已經(jīng)crash了。所以這種方案的缺點(diǎn)很明顯:

  1. MultiDex加載邏輯放在閃屏頁(yè)的話崩泡,閃屏頁(yè)中引用到的類都要配置在主dex禁荒。
  2. ContentProvider必須在主dex,一些第三方庫(kù)自帶ContentProvider允华,維護(hù)比較麻煩圈浇,要一個(gè)一個(gè)配置寥掐。

這時(shí)候就思考一下靴寂,有沒(méi)有其它更好的方案呢?大廠是怎么做的召耘?今日頭條肯定要對(duì)MultiDex進(jìn)行優(yōu)化吧百炬,反編譯瞧瞧?
image

點(diǎn)了一根煙之后污它,開(kāi)始偷代碼…

MultiDex優(yōu)化方案2:今日頭條方案

今日頭條沒(méi)有加固剖踊,反編譯后很容易通過(guò)關(guān)鍵字搜索找到MultidexApplication這個(gè)類,

image

看注釋1的d.a(this);這個(gè)方法衫贬,代碼雖然混淆了德澈,但是方法內(nèi)部的代碼還是可以看出是干嘛的,繼續(xù)跟這個(gè)方法固惯,為了不影響閱讀梆造,我對(duì)混淆做了一些處理,改成正常的方法名葬毫。

image

每個(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方法

image

邏輯如下:
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í)文件代碼

image

while循環(huán)代碼
image

LoadDexActivity 只有一個(gè)加載框手销,加載完再跳轉(zhuǎn)到閃屏頁(yè)
image

dex加載完應(yīng)該要finish掉當(dāng)前Activity
image

按照上面代碼分析歇僧,今日頭條在5.0以下手機(jī)首次啟動(dòng)應(yīng)該是這樣:

  1. 打開(kāi)桌面圖標(biāo)
  2. 顯示默認(rèn)背景
  3. 跳轉(zhuǎn)到加載dex的界面,展示一個(gè)loading的加載框幾秒鐘
  4. 跳轉(zhuǎn)到閃屏頁(yè)

實(shí)際上是不是這樣呢锋拖,用4.4機(jī)器試下诈悍?
image
image
image

看起來(lái)完全跟猜想的一致,擼幾行代碼驗(yàn)證應(yīng)該不難吧兽埃?
image

點(diǎn)了一根煙之后侥钳,開(kāi)始擼代碼,最終實(shí)現(xiàn)效果如下
image

效果跟今日頭條是一致的柄错,不再重復(fù)分析代碼了舷夺,源碼上傳到github,感興趣的同學(xué)可以參考參考售貌,頭條的方案给猾,值得嘗試~ github.com/lanshifu/Mu…再次梳理一下這種方式:

  1. 在主進(jìn)程Application 的 attachBaseContext 方法中判斷如果需要使用MultiDex,則創(chuàng)建一個(gè)臨時(shí)文件颂跨,然后開(kāi)一個(gè)進(jìn)程(LoadDexActivity)敢伸,顯示Loading,異步執(zhí)行MultiDex.install 邏輯恒削,執(zhí)行完就刪除臨時(shí)文件并finish自己池颈。
  2. 主進(jìn)程Application 的 attachBaseContext 進(jìn)入while代碼塊,定時(shí)輪循臨時(shí)文件是否被刪除钓丰,如果被刪除躯砰,說(shuō)明MultiDex已經(jīng)執(zhí)行完,則跳出循環(huán)携丁,繼續(xù)正常的應(yīng)用啟動(dòng)流程琢歇。
  3. 注意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)化方案:

  1. 在Application 的attachBaseContext 方法里娜庇,啟動(dòng)另一個(gè)進(jìn)程的LoadDexActivity去異步執(zhí)行MultiDex邏輯塔次,顯示Loading。
  2. 然后主進(jìn)程Application進(jìn)入while循環(huán)名秀,不斷檢測(cè)MultiDex操作是否完成
  3. 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

image

這段代碼是今日頭條里面的翠忠,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)化思路。

  1. WebView第一次創(chuàng)建比較耗時(shí)拂共,可以預(yù)先創(chuàng)建WebView牺弄,提前將其內(nèi)核初始化。
  2. 使用WebView緩存池宜狐,用到WebView的地方都從緩存池取势告,緩存池中沒(méi)有緩存再創(chuàng)建,注意內(nèi)存泄漏問(wèn)題抚恒。
  3. 本地預(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è)方法的入口和出口

image

插樁后的代碼如下
image

插樁工具參考: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

image

最后按下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)容:

  1. 應(yīng)用啟動(dòng)流程
  2. 閃屏頁(yè)優(yōu)化
  3. MultiDex 原理分析
  4. ClassLoader 加載一個(gè)類的流程分析
  5. 熱修復(fù)原理
  6. MultiDex優(yōu)化:介紹了兩種方式鸳慈,一種是直接在閃屏頁(yè)開(kāi)個(gè)子線程去加載dex,難維護(hù)喧伞,不推薦走芋;一種是今日頭條的方案,在單獨(dú)一個(gè)進(jìn)程加載dex潘鲫,加載完主進(jìn)程再繼續(xù)翁逞。
  7. 快速啟動(dòng)Activity的方式:預(yù)創(chuàng)建Activity,預(yù)加載數(shù)據(jù)溉仑。
  8. 啟動(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í)踐梳庆,理解為什么要這樣做。

閱讀更多

程序員接私活經(jīng)驗(yàn)總結(jié)

今日頭條屏幕適配方案落地研究

IDEA 的優(yōu)雅調(diào)試卑惜,讓 bug 無(wú)處藏身膏执!

面試官:你分析過(guò)線程池源碼嗎?

相信自己残揉,沒(méi)有做不到的胧后,只有想不到的

在這里獲得的不僅僅是技術(shù)!

image
image

喜歡就給個(gè)“在看

           ------------------------------轉(zhuǎn)載自  研發(fā)部終端  公眾號(hào) ![TIM截圖20191119114430.png](https://upload-images.jianshu.io/upload_images/4876206-b712ad953313a842.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末抱环,一起剝皮案震驚了整個(gè)濱河市壳快,隨后出現(xiàn)的幾起案子碉纳,更是在濱河造成了極大的恐慌畏腕,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榨崩,死亡現(xiàn)場(chǎng)離奇詭異梯啤,居然都是意外死亡竖伯,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)因宇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)七婴,“玉大人,你說(shuō)我怎么就攤上這事察滑〈蚶澹” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵贺辰,是天一觀的道長(zhǎng)户盯。 經(jīng)常有香客問(wèn)我,道長(zhǎng)饲化,這世上最難降的妖魔是什么莽鸭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮吃靠,結(jié)果婚禮上硫眨,老公的妹妹穿的比我還像新娘。我一直安慰自己巢块,他們只是感情好捺球,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著夕冲,像睡著了一般氮兵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上歹鱼,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天泣栈,我揣著相機(jī)與錄音,去河邊找鬼弥姻。 笑死南片,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的庭敦。 我是一名探鬼主播疼进,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼秧廉!你這毒婦竟也來(lái)了伞广?” 一聲冷哼從身側(cè)響起拣帽,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嚼锄,沒(méi)想到半個(gè)月后减拭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡区丑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年拧粪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沧侥。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡可霎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宴杀,到底是詐尸還是另有隱情癣朗,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布婴氮,位于F島的核電站斯棒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏主经。R本人自食惡果不足惜荣暮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望罩驻。 院中可真熱鬧穗酥,春花似錦、人聲如沸惠遏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)节吮。三九已至抽高,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間透绩,已是汗流浹背翘骂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帚豪,地道東北人碳竟。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像狸臣,于是被迫代替她去往敵國(guó)和親莹桅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359