Jack Jiang

          我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
          posts - 499, comments - 13, trackbacks - 0, articles - 1

          本文由愛奇藝技術團隊原創分享,原題《愛奇藝Android客戶端啟動優化與分析》。

          1、引言

          互聯網領域里有個八秒定律,如果網頁打開時間超過8秒,便會有超過70%的用戶放棄等待,對Android APP而言,要求更加嚴格,如果系統無響應時間超過5秒,便會出現ANR,APP可能會被強制關閉,因此,啟動時間作為一個重要的性能指標,關系著用戶的第一體驗。

          愛奇藝安卓APP非常重視啟動速度的優化,本文將從啟動過程、啟動時間測量、啟動優化、以及后續監控等方面分享我們在啟動優化方面積累的經驗。

          相關文章:

          移動端IM實踐:Android版微信如何大幅提升交互性能(一)

          移動端IM實踐:Android版微信如何大幅提升交互性能(二)

          移動端IM實踐:iOS版微信界面卡頓監測方案

          微信團隊原創分享:Android內存泄漏監控和優化技巧總結

          美圖App的移動端DNS優化實踐:HTTPS請求耗時減小近半

          (本文同步發布于:http://www.52im.net/thread-2221-1-1.html

          2、啟動模式

          要準確的測量APP的啟動時間,首先我們要了解APP整個啟動過程。

          啟動過程,一般可以分為以下三類:

          從上圖可以看出,啟動過程中,Cold的模式下,生命周期中做的事情最多,啟動的時間最長。因此,我們以冷啟動來衡量APP啟動時間。

          那么啟動過程中,如何判斷哪些生命周期影響啟動速度呢?請繼續往下讀。

          3、啟動過程

          我們知道,APP的啟動和運行,就是Linux系統創建進程和組件對象,并在UI線程中處理組件消息的過程。

          啟動過程圖:

          App的啟動過程,可以劃分為三個階段,下面就各個階段進行詳細講解。

          3.1 創建進程

          當APP啟動時,如果當前app的進程不存在,便會創建新的進程;App主進程啟動后,如果啟動某個組件,并且該組件設置了android:process屬性,組件所運行的進程不存在,也會創建新的進程。

          需要注意的是,如果在啟動階段,初始化的組件中,包含了多個進程,便會創建多次進程,BindApplication操作也會重復執行多次

          3.2 創建UI線程及Handler

          進程創建后,會通過反射,執行ActivityThread入口函數,創建Handler,并在當前線程中prepareMainLooper,并在Handler中接收組件的消息。

          我們來看一下Handler中處理的消息:

          1)LAUNCH_ACTIVITY:啟動,執行Activity;

          2)RESUME_ACTIVITY:恢復Activity;

          3)BIND_APPLICATION,啟動app;

          4)BIND_SERVICE:Service創建, onBind;

          5)LOW_MEMORY:內存不足,回收后臺程序。

          sMainThreadHandler中,處理的消息很多,這里只羅列了,可能在啟動階段可能會執行的操作, 這些操作都是運行在Main Thread中,對啟動而言,屬于阻塞性的。

          Activity生命周期,自然需要在啟動階段執行,但,對于Service的創建,Trim_memory回調,廣播接收等操作,就需要重點考慮,其操作耗時性。

          3.3 Activity運行及繪制

          前兩個過程,創建進程和UI線程及Handler,都是由系統決定的,對APP開發者而言,并不能控制其執行時間,在本階段,執行BindApplication,和Acitivity生命周期,都是可以由開發者自定義。

          Activity執行到onResume之后,會執行至ViewRootImpl,執行兩次performTraversals,第二次traversal操作中,會執行performDraw操作,同時通知RenderThread線程執行繪制。

          從啟動的三個階段,我們可以看出,啟動啟動時間的長短的決定因素在于:主線程中所做事情消耗的時間的多少。

          所以:我們的優化工作主要集中在,排查主線程中耗時性的工作,并進行合理的優化。

          Android手機,系統的資源是有限的,過多的異步線程,會搶占CPU,導致主線程執行時間片間隔增大。同樣的,內存消耗狀態,GC頻率,也會影響啟動的時間。

          4、分析及測量

          通過上述的源碼的解讀,我們已經了解了啟動過程,以及可能引起啟動過慢的原因。接下來介紹一些常用的分析手段及時間測量方法。

          我們的啟動分析工具主要使用SysTrace,具體的使用方法請參考官網文檔:https://developer.android.com/studio/command-line/systrace。

          ▲ Andriod上的各種各樣分析工具,請自行選用

          4.1 SysTrace分析技巧

          【4.1.1、UI Thread 顏色顯示】

          綠色:Running

          白色:Sleeping

          棕色:Uninterruptible Sleep

          橙色:Uninterruptible Sleep - Block I/O

          其中10ms以內的,較短時間的Sleeping狀態,不用關注,可能是由于CPU調度的時間片分配間隔引起的;較長時間的Block I/O和Sleep狀態,可以確定有阻塞啟動的邏輯在這個階段運行,需要進一步對代碼進行分析定位。

          【4.1.2、查看CPU狀態及線程運行時長】

          查看CPU占用狀態:

          線程執行:

          通過該階段密集程度,反映出CPU占用率,也能在一定程度上反映出該階段執行時間被阻塞情況;線程執行情況統計,可以查看線程執行時間排名,對執行時間較長的子線程進行優化。

          4.2 SysTrace啟動時間

          在SysTrace圖中,UI Thread中包含了bindApplication,activityStart,traversal等操作,RenderThread中包含DrawFrame等操作。這些TAG節點是源碼已經添加的,可參考#3.2中介紹。

          Trace上啟動時間:

          從bindApplication至第二次traversal完成,可認為UI第一次繪制完成,啟動完成。選中開始點和結束點,可以查看過程消耗的時間。

          4.3 adb shell am start -W

          在統計APP啟動時間時,系統為我們提供了adb命令,可以輸出啟動時間

          TotalTime: 

          表示新應用啟動的耗時,包括新進程的啟動和 Activity 的啟動,但不包括前一個應用 Activity pause 的耗時。

          系統在繪制完成后,ActivityManagerService會回調該方法,統計時間不如SysTrace準確,但是能夠方便我們通過腳本多次啟動測量TotalTime,對比版本間啟動時間差異。

          4.4 埋點

          通過APP啟動生命周期中,關鍵位置加入時間點記錄,達到測量目的。

          4.5 錄屏

          錄屏方式收集到的時間,更接近于用戶的真實體感。

          5、優化總結

          為了讓用戶在進入APP之后,更快更流暢的使用服務,所以會在啟動過程中,提前對一些基礎庫和組建進行初始化操作,這就意味著系統有限的資源會被搶占,影響啟動時間。啟動時間的優化,是一個平衡性能和體驗的過程。

          通過Systrace工具分析,我們發現愛奇藝愛奇藝安卓APP啟動過程中一些問題,接下來,我們就結合具體的業務實踐,進行啟動問題進行優化。

          5.1 區分進程初始化Application

          由第3章我們了解到,對于一個app而言,App內組件可以運行在不同的進程之中。

          舉個例子: 

          一個APP擁有主進程,插件進程,下載進程三個進程,會在啟動階段創建相應的組件,但只有一個QYApplication繼承自系統Application,創建三次進程,QYApplication中attach(),onCreate()方法都會被執行三次。

          每個進程說需要初始化的內容肯定是不一樣的,所以,為了防止資源的浪費,我們需要區分進程,初始化Appcation.

          成果:對多進程應用而言,通過對初始化內容進行梳理,合理區分初始化,會大幅減少內存和CPU占用。

          5.2 異步處理耗時任務

          子線程處理耗時任務,主線程做的事情越少,越早進入Acitivity繪制階段,界面越早展現。

          注意:

          1)不在主線程做耗時任務,如文件,網絡等;

          2)啟動階段初始化任務,盡量在異步線程處理;

          3)主線程,不用等待或者依賴于子線程任務。

          進一步優化:可以自建線程池,維持一定線程個數,管理任務隊列。

          5.3 防止多線程搶占CPU

          Android系統資源有限,特別是CPU資源,理論上來說,UI線程執行的任務,也無法保證一直被調度狀態,當并發的線程數過多,UI線程時間片會更短,從而導致啟動時間被變慢。

          下面羅列一些常見,容易造成CPU被搶占的場景:

          成果:通過對執行時間較久,執行頻率的業務進行優化,將CPU占有率維持在合理的程度,會大幅減少啟動時間,減少300ms以上。

          5.4 系統API使用

          部分系統的API使用是阻塞性的,文件很小可能無法感知,當文件過大,或者使用頻繁時,可能造成阻塞。

          例如:

          1)SharedPreference.Editor提交操作:

            - commit方法屬于屬于阻塞性質API,建議使用apply;

            - 此外,我們知道,SP文件的存儲是一個XML文件,以key-value形式存儲,當業務過多時,需要拆分為多個文件存儲,防止文件過大,出現讀取耗時及ANR;

            - 進一步優化,可對啟動階段,頻繁的SP操作在內存中,統一提交。

          2)AssetManager.open操作: 

          Android開發中,我們有時會將資源文件放在assets目錄中,然后使用open操作讀取文件,如果文件過大,需要在異步線程中執行。

          成果:隨著業務量日積月累,正常的系統API的使用,也可能出現問題,通過排除,可減少50-100ms。

          5.5 精簡布局

          布局的復雜程度,直接影響繪制的時間。

          舉個例子:

          在啟動過程中,會有需要大的背景圖,只有第一次安裝時使用,后續屬性設置為android:visibility="gone",但是,雖然設置了gone屬性,不會顯示,但依舊會被解析。

          建議:

          1)減少布局層次;

          2)無用資源使用ViewStub,使用時加載。

          成果:啟動階段的布局較簡單,通過優化背景圖片的加載,減少50-100ms。

          5.6 Service延后初始化

          App啟動中過程中,經常進行Service初始化操作,由于Service使用一般不涉及界面,可能會認為初始化生命周期不在主線程中,其實不然,在3.2的啟動過程源碼介紹中講到,Service的生命周期,也屬于主線程Handler接收的Message之一。

          建議:Service生命周期中,注意邏輯執行時間性能優化,初始化盡量延后。

          成果:取決于初始化Service的生命周期執行時間,可減少200ms以上。

          5.7 將任務delay至首頁繪制完成后

          對于APP首頁展示不需要的初始化邏輯,可延后至首頁繪制完成后初始化。

          注意:需要post兩次才能保證在第一次繪制之后顯示,因為,系統繪制會執行兩次Performtraversal。

          進一步優化:可將業務邏輯的初始化劃分為,首頁繪制后,5s,10s,20s三個階段分別初始化,防止首頁繪制執行任務過多造成掉幀。

          成果:釋放繪制階段的CPU,可將復雜的繪制提前200ms以上。

          6、性能監控

          穩定的用戶體驗依賴于持續的監控,愛奇藝為監控啟動性能建立了一套監控體系、測試、工具、開發等幾個團隊從不同的緯度搭建不同的監控方案。

          監控方案如下:

          1)測試:錄屏,從用戶的真實體驗角度,獲取最準確的啟動時間;

          2)實時監控:通過埋點,大數據采樣投遞獲取真實線上環境數據,從地域,時間,機型,app版本,系統版本等各個緯度對啟動時間進行監控;

          3)腳本測試:通過對腳本,對同一收集多次啟動數據進行收集,通過不同版本間的對比,監控啟動時間的變化情況。

          7、SysTrace擴展

          SysTrace通過TAG節點可以清晰展現,啟動過程以及方法執行時間,但是,從發現問題,然后通過節點去定位問題,是一件很繁瑣的工作,如果你們工程編譯又比較慢,簡直讓人崩潰。

          比如:自動化TAG注入。

          在Android工程編譯的過程中,指定class,在方法前后,自動化插入Trace節點,統計方法執行時間。

          流程:

          1)在編譯的過程中,插入自定義Task任務;

          2)讀取配置文件,文件中包含了需要注入java文件名和路徑名和method;

          3)找到需要注入的class文件,然后通過ASM改變字節碼,方法前后,插入自定義自定義方法。

          通過工具的操作,能夠做到不用修改原有工程文件,自動在打包時注入TAG節點和邏輯代碼,配置文件可以循環利用,提高分析效率,節能環保。

          8、優化成果

          啟動時間,由于不同的機型性能同,Android系統版本不同,同一APP版本啟動時間,相差很大,所以統計一般以同一手機,不同版本做比較,盡量保證手機狀態一致。

          SysTrace手機優化時間對比:

          腳本多次啟動時間收集對比:

          經過多個版本的持續優化,有無廣告兩種不同的場景下,啟動時間分別減少40%和35%,啟動速度得到了較大的提升。

          9、本文小結

          啟動時間的優化和監控,是一項長期的任務,需要對異常的情況進行分析,對可能造成阻塞的代碼邏輯進行合理的優化,非常感謝各個業務團隊支持和配合。

          以上就是全部啟動時間優化相關的內容,謝謝大家能夠閱讀到這里,如果有更好的建議,歡迎交流!

          附錄:更多移動端高級開發技術分享

          全面了解移動端DNS域名劫持等雜癥:技術原理、問題根源、解決方案等

          金蝶隨手記團隊分享:還在用JSON? Protobuf讓數據傳輸更省更快(原理篇)

          金蝶隨手記團隊分享:還在用JSON? Protobuf讓數據傳輸更省更快(實戰篇)

          騰訊技術分享:社交網絡圖片的帶寬壓縮技術演進之路

          通俗易懂:基于集群的移動端IM接入層負載均衡方案分享

          QQ音樂團隊分享:Android中的圖片壓縮技術詳解(上篇)

          QQ音樂團隊分享:Android中的圖片壓縮技術詳解(下篇)

          騰訊原創分享(一):如何大幅提升移動網絡下手機QQ的圖片傳輸速度和成功率

          騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(上篇)

          騰訊原創分享(三):如何大幅壓縮移動網絡下APP的流量消耗(下篇)

          基于社交網絡的Yelp是如何實現海量用戶圖片的無損壓縮的?

          騰訊技術分享:騰訊是如何大幅降低帶寬和網絡流量的(圖片壓縮篇)

          騰訊技術分享:騰訊是如何大幅降低帶寬和網絡流量的(音視頻技術篇)

          最火移動端跨平臺方案盤點:React Native、weex、Flutter

          iPhone X 的 UI界面適配官方指南!》

          新浪微博技術分享:微博短視頻服務的優化實踐之路

          全面掌握移動端主流圖片格式的特點、性能、調優等

          邁向高階:優秀Android程序員必知必會的網絡基礎

          HTTPS時代已來,打算更新你的HTTP服務了嗎?

          移動端APP的日志上報機制的優化實踐

          移動端網絡優化之HTTP請求的DNS優化

          偽即時通訊:分享滴滴出行iOS客戶端的演進過程

          Android版微信從300KB到30MB的技術演進(PPT講稿) [附件下載]

          微信團隊原創分享:Android版微信從300KB到30MB的技術演進

          Android程序員的痛你永遠不懂(一):Bitmap到底占用多大內存?

          Android程序員的痛你永遠不懂(二):如何減少Bitmap內存占用?

          Android反編譯利器APKDB:沒有美工的日子里繼續堅強的擼

          全面總結iOS版微信升級iOS9遇到的各種“坑”

          微信團隊原創資源混淆工具:讓你的APK立減1M

          微信團隊原創Android資源混淆工具:AndResGuard [有源碼]

          Android版微信安裝包“減肥”實戰記錄

          iOS版微信安裝包“減肥”實戰記錄

          iOS端移動網絡調優的8條建議

          微信“紅包照片”背后的技術難題

          移動端IM實踐:iOS版微信小視頻功能技術方案實錄

          移動端IM實踐:iOS版微信的多設備字體適配方案探討

          愛奇藝技術分享:愛奇藝Android客戶端啟動速度優化實踐總結

          >> 更多同類文章 ……

          (本文同步發布于:http://www.52im.net/thread-2221-1-1.html



          作者:Jack Jiang (點擊作者姓名進入Github)
          出處:http://www.52im.net/space-uid-1.html
          交流:歡迎加入即時通訊開發交流群 215891622
          討論:http://www.52im.net/
          Jack Jiang同時是【原創Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
          本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 通山县| 仙居县| 金乡县| 博客| 房山区| 仲巴县| 泉州市| 阿克苏市| 万安县| 永修县| 定安县| 罗江县| 云林县| 巴林左旗| 庄河市| 博客| 曲麻莱县| 界首市| 图木舒克市| 长宁区| 庆元县| 张家口市| 曲阜市| 新晃| 安仁县| 额济纳旗| 万山特区| 修水县| 岑巩县| 兴化市| 筠连县| 周至县| 平遥县| 天长市| 赤水市| 雷波县| 桂平市| 瓮安县| 甘谷县| 府谷县| 西吉县|