午夜精品人妻久久久-成年美女很黄的网站-在线看片免费人成视久网app-国产精品美女无遮挡一区二区-91精品国产综合久久久久-国产的免费视频又猛又爽又刺激-在线看片免费人成视久网app-久久香蕉国产精品视频-av一区二区三区高清

二 Android性能優(yōu)化系列篇:啟動優(yōu)化

強調(diào)一下:性能優(yōu)化的開發(fā)文檔跟之前的面試文檔一樣 , 想要的跟作者直接要 。
二、啟動優(yōu)化2.1 我們?yōu)槭裁匆鰡觾?yōu)化?
用戶希望應用能夠快速打開 。啟動時間過長的應用不能滿足這個期望 , 并且可能會令用戶失望 。輕則鄙視你,重則直接卸載你的應用 。
用戶不會在乎你的項目是不是過大,里面是不是有很多初始化的邏輯 。他只在乎你-慢了 。
所以咱們這篇文章有兩個目的:
今天咱們就來了解一下應用啟動內(nèi)部機制和啟動速度優(yōu)化 。
2.2 啟動內(nèi)部機制
應用有三種啟動狀態(tài):
2.2.1 冷啟動
冷啟動是指應用從頭開始:冷啟動發(fā)生在設備啟動后第一次啟動應用程序 (>fork>app)  , 或系統(tǒng)關閉應用程序后 。
在冷啟動開始時 , 系統(tǒng)有三個任務 。這些任務是:
一旦系統(tǒng)創(chuàng)建了應用程序進程 , 應用程序進程就負責接下來的階段:
如下圖:
注意:在創(chuàng)建和創(chuàng)建期間可能會出現(xiàn)性能問題 。
創(chuàng)建
當應用程序啟動時,空白啟動頁面保留在屏幕上,直到系統(tǒng)首次完成應用程序的繪制 。
如果你重寫了.(),系統(tǒng)將調(diào)用 上的()方法 。之后,應用程序生成主線程 , 也稱為UI線程java簡單實現(xiàn)一個阻塞隊列 , 并將創(chuàng)建主的任務交給它 。
創(chuàng)建
應用進程創(chuàng)建你的后,會執(zhí)行以下操作:
注意:() 方法對加載時間的影響最大,因為它執(zhí)行開銷最高的工作:加載UI的布局和渲染,以及初始化運行所需的對象 。
2.2.2 熱啟動
熱啟動時,系統(tǒng)將應用從后臺拉回前臺 , 應用程序的在內(nèi)存中沒有被銷毀,那么應用程序可以避免重復對象初始化,UI的布局和渲染 。
如果被銷毀則需要重新創(chuàng)建 。
和冷啟動的區(qū)別: 不需要創(chuàng)建。
2.2.3 溫啟動
溫啟動介于冷啟動和熱啟動中間吧 。例如:
咱們看看他們共同消耗多長時間 。
2.3 查詢的啟動時間2.3.1 初始顯示時間(Time to)
在4.4(API 級別 19)及更高版本中,包含一個輸出行,其中包含一個名為的值 。此值表示啟動流程和完成在屏幕上繪制相應活動之間經(jīng)過的時間量 。經(jīng)過的時間包含以下事件序列:
注意這里查看日志需要如下操作:
報告的日志行類,如下圖:
//冷啟動I/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +1s355ms//溫啟動(進程被殺死)I/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +1s46ms//熱啟動I/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +289msI/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +253ms
圖例講解:
第一個時間,冷啟動時間:+ 。
然后我們在后臺殺死進程,再次啟動應用;
第二個時間 , 溫啟動時間:+ 。
這里咱們在后臺殺死進程所以:應用進程和需要重新啟動 。
第三個時間:熱啟動時間:+289ms 和 +253ms
按返回鍵,僅退出 。所以耗時比較短 。
當然整體看這個應用開啟時間并不長,因為 Demo 的和都沒有進行太多的操作 。
2.3.2 完全顯示時間(Time to full )
你可以使用 () 方法來測量應用程序啟動和所有資源和視圖層次結構的完整顯示之間經(jīng)過的時間 。在應用程序執(zhí)行延遲加載的情況下,這可能很有價值 。在延遲加載中,應用程序不會阻止窗口的初始繪制,而是異步加載資源并更新視圖層次結構 。
這里我在.()中加了個工作線程 。并在里面調(diào)用() 方法 。代碼如下:
@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.e(this.getClass().getName(), "onCreate");setContentView(R.layout.activity_main);...new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(3000);reportFullyDrawn();} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
報告的日志行類,如下圖:
I/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +3s970msI/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +3s836msI/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +3s107msI/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +3s149ms
圖例講解:
然后你會發(fā)現(xiàn)界面出來好一會才打這個日志 。看到這里我覺得好多人已經(jīng)知道怎么去優(yōu)化啟動速度了 。
2.4 性能遲緩分析
看到上面的實驗其實三種啟動情況,受我們影響的方面在于和。
2.4.1 繁瑣的 初始化
當你的代碼覆蓋對象并在初始化該對象時執(zhí)行繁重的工作或復雜的邏輯時 , 啟動性能可能會受到影響 。產(chǎn)生的原因包括:
解決方案
無論問題在于不必要的初始化還是磁盤I/O,解決方案都是延遲初始化 。換句話說 , 你應該只初始化立即需要的對象 。不要創(chuàng)建全局靜態(tài)對象,而是轉向單例模式,應用程序只在第一次需要時初始化對象 。
此外,考慮使用依賴注入框架(如Hilt)
2.4.2 繁瑣的初始化
活動創(chuàng)建通常需要大量高開銷工作 。通常,有機會優(yōu)化這項工作以實現(xiàn)性能改進 。
產(chǎn)生的原因包括:
解決方案如下 。
布局優(yōu)化
具體內(nèi)容請看后文布局優(yōu)化 。
代碼優(yōu)化
具體內(nèi)容請看后文代碼優(yōu)化 。
2.5 阻塞實驗
2.5.1阻塞 2秒,阻塞 2秒 。
.class
public class SccApp extends Application {@RequiresApi(api = Build.VERSION_CODES.P)@Overridepublic void onCreate() {super.onCreate();String name = getProcessName();MLog.e("ProcessName:"+name);getProcessName("com.scc.demo");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}
.class
publicclass MainActivity extends ActivityBase implements View.OnClickListener {@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.e(this.getClass().getName(), "onCreate");setContentView(R.layout.activity_main);...try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(3000);reportFullyDrawn();} catch (InterruptedException e) {e.printStackTrace();}}}).start();}}
報告的日志,如下:
//冷啟動I/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +5s458msI/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +8s121ms//溫啟動(進程被殺死)I/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +5s227msI/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +7s935ms//熱啟動I/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +2s304msI/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +5s189msI/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +2s322msI/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +5s169ms
將和阻塞的2秒都放在工作線程去操作
這個就是把代碼放在如下代碼中執(zhí)行即可,就不全部貼出來了 。
new Thread(new Runnable() {@Overridepublic void run() {...}}).start();
【二 Android性能優(yōu)化系列篇:啟動優(yōu)化】運行結果如下:
//冷啟動I/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +1s227msI/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +3s957ms//溫啟動(進程被殺死)I/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +1s83msI/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +3s828ms//熱啟動I/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +324msI/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +3s169msI/ActivityTaskManager: Displayed com.scc.demo/.actvitiy.MainActivity: +358msI/ActivityTaskManager: Fully drawn com.scc.demo/.actvitiy.MainActivity: +3s207ms
2.6 APP 啟動黑/白屏
應用啟動時java簡單實現(xiàn)一個阻塞隊列 , 尤其是大型應用, 經(jīng)常出現(xiàn)幾秒鐘的黑屏或白屏,黑屏或白屏取決于主界面的主題風格 。
2.6.1 優(yōu)雅的解決黑白屛
應用啟動時很多大型應用都會有一個廣告(圖片及視頻)頁或閃屏頁(2-3S) 。這并不是開發(fā)者想要放上去的 , 而是為了避免上述啟動白屏導致用戶體很差 。當然你可以珍惜這2-3秒做一個異步加載或者請求 。
寫到這里 。應用啟動模式、啟動時間、啟動速度優(yōu)化算是完事了 。當然后面如果有更好的優(yōu)化方案還會繼續(xù)補充 。
2.7 啟動階段抑制GC
啟動時CG抑制,允許堆一直增長,直到手動或OOM停止GC抑制 。(空間換時間)
前提條件實現(xiàn)原理缺點
需要白名單覆蓋所有設備,但維護成本高 。
2.8 CPU鎖頻
一個設備的CPU通常都是4核或者8核,但是應用在一般情況下對CPU的利用率并不高,可能只有30%或者50%,如果我們在啟動速度暴力拉伸CPU頻率 , 以此提高CPU的利用率,那么,應用的啟動速度會提升不少 。
在系統(tǒng)中,CPU相關的信息存儲在/sys///cpu目錄的文件中,通過對該目錄下的特定文件進行寫值,實現(xiàn)對CPU頻率等狀態(tài)信息的更改 。
缺點
暴力拉伸CPU頻率,導致耗電量增加 。
CPU工作模式CPU的工作頻率范圍
對應的文件有:
2.9 IO優(yōu)化
這里需要注意的是 , 需要考慮重度用戶的使用場景 。
補充加油站:Linux IO知識
1、磁盤高速緩存技術
利用內(nèi)存中的存儲空間來暫存從磁盤中讀出的一系列盤塊中的信息 。因此,磁盤高速緩存在邏輯上屬于磁盤,物理上則是駐留在內(nèi)存中的盤塊 。
其內(nèi)存中分為兩種形式:
2、分頁
3、高速緩存/緩沖器
4、linux同步IO:sync、fsync、msync、
為什么要使用同步IO?
當數(shù)據(jù)寫入文件時,內(nèi)核通常先將該數(shù)據(jù)復制到緩沖區(qū)高速緩存或頁面緩存中,如果該緩沖區(qū)尚未寫滿,則不會將其排入輸入隊列,而是等待其寫滿或內(nèi)核需要重用該緩沖區(qū)以便存放其他磁盤塊數(shù)據(jù)時 , 再將該緩沖排入輸出隊列,最后等待其到達隊首時,才進行實際的IO操作—延遲寫 。
延遲寫減少了磁盤讀寫次數(shù),但是卻降低了文件內(nèi)容的更新速度 , 可能會造成文件更新內(nèi)容的丟失 。為了保證數(shù)據(jù)一致性,則需使用同步IO 。
sync
fsync
msync
如果當前硬盤的平均尋道時間是3-15ms,硬盤的平均旋轉延遲大約為4ms,因此一次IO操作的耗時大約為10ms 。
如果使用內(nèi)存映射文件的方式進行文件IO(mmap),將文件的page cache直接映射到進程的地址空間,這時需要使用msync系統(tǒng)調(diào)用確保修改的內(nèi)容完全同步到硬盤之上 。
日志文件都是追加性的 , 文件尺寸一致在增大,如何利用好減少日志文件的同步開銷?
創(chuàng)建每個log文件時先寫文件的最后一個page,將log文件擴展為10MB大?。?這樣便可以使用,每寫10MB只有一次同步的開銷 。

二 Android性能優(yōu)化系列篇:啟動優(yōu)化

文章插圖
二 Android性能優(yōu)化系列篇:啟動優(yōu)化

文章插圖
2.10 磁盤IO與網(wǎng)絡IO
磁盤IO(緩存IO)
標準IO,大多數(shù)文件系統(tǒng)默認的IO操作 。
優(yōu)點
缺點
DMA方式可以將數(shù)據(jù)直接從磁盤讀到頁緩存中,或者將數(shù)據(jù)從頁緩存中寫回到磁盤,而不能在應用程序地址空間和磁盤之間進行數(shù)據(jù)傳輸,這樣,數(shù)據(jù)在傳輸過程中需要在應用程序地址空間(用戶空間)和緩存(內(nèi)核空間)中進行多次數(shù)據(jù)拷貝操作 , 這帶來的CPU以及內(nèi)存開銷是非常大的 。
磁盤IO主要的延時(硬盤為例)
機械轉動延時(平均2ms)+ 尋址延時(2~3ms)+ 塊傳輸延時(0.1ms左右)=> 平均5ms
網(wǎng)絡IO主要延時
服務器響應延時 + 帶寬限制 + 網(wǎng)絡延時 + 跳轉路由延時 + 本地接收延時(一般為幾十毫秒到幾千毫秒,受環(huán)境影響極大)
2.11 PIO與DMA
PIO
很早之前,磁盤和內(nèi)存之間的數(shù)據(jù)傳輸是需要CPU控制的,也就是讀取磁盤文件到內(nèi)存中時,數(shù)據(jù)會經(jīng)過CPU存儲轉發(fā),這種方式稱為PIO 。
DMA(直接內(nèi)存訪問,)
2.12 直接IO與異步IO
直接IO
應用程序直接訪問磁盤數(shù)據(jù),而不經(jīng)過內(nèi)核緩沖區(qū) 。以減少從內(nèi)核緩沖區(qū)到用戶數(shù)據(jù)緩存的數(shù)據(jù)復制 。
異步IO
當訪問數(shù)據(jù)的線程發(fā)出請求后,線程會接著去處理其它事情,而不是阻塞等待 。
2.13 數(shù)據(jù)重排
Dex文件用到的類和APK里面各種資源文件都比較?。?寥∑搗保?掖排痰刂販植擠段П冉瞎?。我們可以利用Linux文件IO流程中的page cache機制將它們按照讀取順序重新排列在一起,以減少真實的磁盤IO次數(shù) 。
2.13.1 類重排
使用的
ReDex //re…
的調(diào)整類在Dex中的排列順序 。
2.13.2 資源文件重排2.14 類加載優(yōu)化()2.14.1 類預加載原理
對象第一次創(chuàng)建的時候 , JVM首先檢查對應的Class對象是否已經(jīng)加載 。如果沒有加載,JVM會根據(jù)類名查找.class文件 , 將其Class對象載入 。同一個類第二次new的時候就不需要加載類對象 , 而是直接實例化 , 創(chuàng)建時間就縮短了 。
2.14.2 類加載優(yōu)化過程
ART比較復雜 , Hook需要兼容幾個版本 。而且在安裝時 , 大部分Dex已經(jīng)優(yōu)化好了,去掉ART平臺的只會對動態(tài)加載的Dex帶來一些好處 。所以暫時不建議在ART平臺使用 。
2.14.3 延伸:插件化和熱修復
它們在設計上都存在大量的Hook和私有API調(diào)用,共同的缺點有如下兩類問題 。
1、穩(wěn)定性較差
由于廠商的兼容性、安裝失敗、ART加載時失敗等原因,還是會有一些代碼和資源的異常 。P推出的non-sdk-調(diào)用限制,以后適配只會越來越難,成本越來越高 。
2、性能問題
用到一些黑科技導致底層的優(yōu)化享受不到 。如加載補丁后,啟動速度會降低5%~10% 。
2.14.4 各項熱補丁技術的優(yōu)缺點
缺點
優(yōu)點
2.14.5 實現(xiàn)機制
官方使用熱補丁技術實現(xiàn) 。
應用構建流程
構建 -> 部署 -> 安裝 -> 重啟app -> 重啟
實現(xiàn)目標
盡可能多的剔除不必要的步驟,然后提升必要步驟的速度 。
構建的三種方式
1、
增量構建 -> 改變部署
場景:
適用于多數(shù)簡單的改變(包括一些方法實現(xiàn)的修改,或者變量值修改) 。
2、Warm Swap
增量構建 -> 改變部署 -> 重啟
場景:
一般是修改了 。
3、Cold Swap
增量構建 -> 改變部署 -> 應用重啟 -> 重啟
場景:
涉及結構性變化,如修改了繼承規(guī)則或方法簽名 。
首次運行 Run,執(zhí)行的操作
原理
運行著任務來生成增量.dex文件(dex對應著開發(fā)中的修改類),AS會提取這些.dex文件發(fā)送到App ,然后部署到App 。因為原來版本的類都裝載在運行中的程序了 , 會解釋更新好這些.dex文件,發(fā)送到App 的時候,交給自定義的類加載器來加載.dex文件 。App 會不斷地監(jiān)聽是否需要重寫類文件,如果需要,任務會被立馬執(zhí)行 , 新的更改便能立即被響應 。
需要注意的是 , 此時是不能回退的,必須重啟應用響應修改 。
原理
因為資源文件是在創(chuàng)建時加載,所以必須重啟加載資源文件 。
注意:的值是在APK安裝的時候被讀取的,所以需要觸發(fā)一個完整的應用構建和部署 。
原理
應用部署的時候,會把工程拆分成十個部分 , 每個部分都擁有自己的.dex文件,然后所有的類會根據(jù)包名被分配給相應的.dex文件 。當開啟時,修改過的類所對應的的.dex文件,會重組生成新的.dex文件,然后再部署到設備上 。
注意:應用多進程會被降級為 。
2.15 ASM字節(jié)碼插樁
插樁就是將一段代碼插入或者替換原本的代碼 。字節(jié)碼插樁就是在我們的代碼編譯成字節(jié)碼(Class)后 , 在下生成dex之前修改Class文件,修改或者增強原有代碼邏輯的操作 。
除了、框架外,還有一個應用更為廣泛的ASM框架同樣也是字節(jié)碼操作框架,Run包括就是借助ASM來實現(xiàn)各自的功能 。
可以這樣理解Class字節(jié)碼與ASM之間的聯(lián)系 , 即JSON對于GSON就類似于字節(jié)碼Class對于/ASM 。
ASM自動埋點方案實踐
1.5.0版本以后提供了 API,允許第三方在打包dex文件之前的編譯過程中操作.class文件,我們做的就是實現(xiàn)進行.class文件遍歷拿到所有方法,修改完成后對文件進行替換 。
大致的流程如下所示:
1、自動埋點追蹤 , 遍歷所有文件更換字節(jié)碼
AutoTransform -> transform -> inputs.each {TransformInput input -> input.jarInput.each { JarInput jarInput -> … } input.directoryInputs.each { DirectoryInput directoryInput -> … }}
2、插件實現(xiàn)
PluginEntry -> apply -> def android = project.extensions.getByType(AppExtension)registerTransform(android) -> AutoTransform transform = new AutoTransform?android.registerTransform(transform)
3、使用ASM進行字節(jié)碼編寫
ASM框架核心類
1、visit -> 在中根據(jù)判斷是否是實現(xiàn)View$接口的類 , 只有滿足條件的類才會遍歷其中的方法進行操作 。
2、在中對該方法進行修改
visitAnnotation -> onMethodEnter -> onMethodExit
3、先在java文件中編寫要插入的代碼,然后使用ASM插件查看對應的字節(jié)碼,根據(jù)其用ASM提供的Api一一對應地把代碼填進來即可 。
2.16
原理
的粒度是Dex格式的每一項 , 的粒度是文件 , /Qzone的粒度為class 。
缺點
熱補丁方案對比
若不care性能損耗與補丁包大??,Qzone是最簡單且成功率最高的方案 。
2.17 完善的熱補丁系統(tǒng)構建
一、網(wǎng)絡通道
負責將補丁包交付給用戶 , 包括特定用戶和全量用戶 。
1、pull通道
在登錄/24小時等時機,通過pull方式查詢后臺是否有對應的補丁包更新 。
2、指定版本的push通道
在緊急情況下,我們可以在一個小時內(nèi)向所有用戶下發(fā)補丁包更新 。
3、指定特定用戶的push通道
對特定用戶或用戶組做遠程調(diào)試 。
二、上線與管理平臺
快速上線,管理歷史記錄,以及監(jiān)控補丁的運行情況 。
2.18 啟動優(yōu)化的常見問題(重要?。。?、啟動優(yōu)化是怎么做的?
在某一個版本之后呢 , 我們會發(fā)現(xiàn)這個啟動速度變得特別慢,同時用戶給我們的反饋也越來越多,所以,我們開始考慮對應用的啟動速度來進行優(yōu)化 。然后,我們就對啟動的代碼進行了代碼層面的梳理,我們發(fā)現(xiàn)應用的啟動流程已經(jīng)非常復雜,接著 , 我們通過一系列的工具來確認是否在主線程中執(zhí)行了太多的耗時操作 。
我們經(jīng)過了細查代碼之后,發(fā)現(xiàn)應用主線程中的任務太多,我們就想了一個方案去針對性地解決,也就是進行異步初始化 。(引導=>第2題) 然后,我們還發(fā)現(xiàn)了另外一個問題,也可以進行針對性的優(yōu)化,就是在我們的初始化代碼當中有些的優(yōu)先級并不是那么高 , 它可以不放在的中執(zhí)行,而完全可以放在之后延遲執(zhí)行的,因為我們對這些代碼進行了延遲初始化,最后,我們還結合了做了一個更優(yōu)的延遲初始化的方案,利用它可以在主線程的空閑時間進行初始化,以減少啟動耗時導致的卡頓現(xiàn)象 。做完這些之后,我們的啟動速度就變得很快了 。
最后,我簡單說下我們是怎么長期來保持啟動優(yōu)化的效果的 。首先,我們做了我們的啟動器,并且結合了我們的CI,在線上加上了很多方面的監(jiān)控 。(引導=> 第4題)
2、是怎么異步的,異步遇到問題沒有?
我們最初是采用的普通的一個異步的方案,即new+ 設置線程優(yōu)先級為后臺線程的方式在的方法中進行異步初始化,后來 , 我們使用了線程池、的方式,但是,在我們應用的演進過程當中,發(fā)現(xiàn)代碼會變得不夠優(yōu)雅,并且有些場景非常不好處理,比如說多個初始化任務直接的依賴關系,比如說某一個初始化任務需要在某一個特定的生命周期中初始化完成,這些都是使用線程池、無法實現(xiàn)的 。所以說,我們就開始思考一個新的解決方案 , 它能夠完美地解決我們剛剛所遇到的這些問題 。
這個方案就是我們目前所使用的啟動器 , 在啟動器的概念中,我們將每一個初始化代碼抽象成了一個Task,然后,對它們進行了一個排序,根據(jù)它們之間的依賴關系排了一個有向無環(huán)圖,接著,使用一個異步隊列進行執(zhí)行,并且這個異步隊列它和CPU的核心數(shù)是強烈相關的 , 它能夠最大程度地保證我們的主線程和別的線程都能夠執(zhí)行我們的任務,也就是大家?guī)缀醵伎梢酝瑫r完成 。
3、啟動優(yōu)化有哪些容易忽略的注意點?
首先 , 在CPU 和中有兩個很重要的指標,即cpu time與wall time,我們必須清楚cpu time與wall time之間的區(qū)別,wall time指的是代碼執(zhí)行的時間,而cpu time指的是代碼消耗CPU的時間,鎖沖突會造成兩者時間差距過大 。我們需要以cpu time來作為我們優(yōu)化的一個方向 。
其次,我們不僅只追求啟動速度上的一個提升,也需要注意延遲初始化的一個優(yōu)化,對于延遲初始化,通常的做法是在界面顯示之后才去進行加載,但是如果此時界面需要進行滑動等與用戶交互的一系列操作 , 就會有很嚴重的卡頓現(xiàn)象,因此我們使用了來實現(xiàn)cpu空閑時間來執(zhí)行耗時任務,這極大地提升了用戶的體驗,避免了因啟動耗時任務而導致的頁面卡頓現(xiàn)象 。
最后,對于啟動優(yōu)化,還有一些黑科技,首先,就是我們采用了類預先加載的方式,我們在.方法之后起了一個線程,然后用Class.的方式來預先觸發(fā)類的加載,然后當我們這個類真正被使用的時候,就不用再進行類加載的過程了 。同時 , 我們再看圖的時候,有一部分手機其實并沒有給我們應用去跑滿cpu,比如說它有8核,但是卻只給了我們4核等這些情況,然后,有些應用對此做了一些黑科技,它會將cpu的核心數(shù)以及cpu的頻率在啟動的時候去進行一個暴力的提升 。
4、版本迭代導致的啟動變慢有好的解決方式嗎?
這種問題其實我們之前也遇到過,這的確非常難以解決 。但是,我們后面對此進行了反復的思考與嘗試 , 終于找到了一個比較好的解決方式 。
首先,我們使用了啟動器去管理每一個初始化任務,并且啟動器中每一個任務的執(zhí)行都是被其自動進行分配的 , 也就是說這些自動分配的task我們會盡量保證它會平均分配在我們每一個線程當中的,這和我們普通的異步是不一樣的,它可以很好地緩解我們應用的啟動變慢 。
其次,我們還結合了CI,比如說,我們現(xiàn)在限制了一些類,如,如果有人修改了它,我們不會讓這部分代碼合并到主干分支或者是修改之后會有一些內(nèi)部的工具如郵件的形式發(fā)送到我 , 然后,我就會和他確認他加的這些代碼到底是耗時多少,能否異步初始化 , 不能異步的話就考慮延遲初始化,如果初始化時間太長,則可以考慮是否能進行懶加載,等用到的時候再去使用等等 。
然后,我們會將問題盡可能地暴露在上線之前 。同時,我們真正已經(jīng)到了線上的一個環(huán)境下時,我們進行了監(jiān)控的一個完善,我們不僅是監(jiān)控了App的整個的啟動時間,同時呢 , 我們也將每一個生命周期都進行了一個監(jiān)控 。比如說的與方法的耗時,以及這兩個生命周期之間間隔的時間,我們都進行了一個監(jiān)控 , 如果說下一次我們發(fā)現(xiàn)了這個啟動速度變慢了,我們就可以去查找到底是哪一個環(huán)節(jié)變慢了,我們會和以前的版本進行對比,對比完成之后呢 , 我們就可以來找這一段新加的代碼 。
本文到此結束,希望對大家有所幫助 。