說到 Android 系統(tǒng)手機(jī),大部分人的印象是用了一段時(shí)間就變得有點(diǎn)卡頓,有些程序在運(yùn)行期間莫名其妙的出現(xiàn)崩潰,打開系統(tǒng)文件夾一看,發(fā)現(xiàn)多了很多文件,然后用手機(jī)管家 APP 不斷地進(jìn)行清理優(yōu)化 ,才感覺運(yùn)行速度稍微提高了點(diǎn),就算手機(jī)在各種性能跑分軟件面前分?jǐn)?shù)遙遙領(lǐng)先,還是感覺無論有多大的內(nèi)存空間都遠(yuǎn)遠(yuǎn)不夠用。相信每個(gè)使用 Android 系統(tǒng)的用戶都有過以上類似經(jīng)歷,確實(shí),Android 系統(tǒng)在流暢性方面不如 IOS 系統(tǒng),為何呢,明明在看手機(jī)硬件配置上時(shí),Android 設(shè)備都不會(huì)輸于 IOS 設(shè)備,甚至都強(qiáng)于它,關(guān)鍵是在于軟件上。
造成這種現(xiàn)象的原因是多方面的,簡單羅列幾點(diǎn)如下:
? ? 其實(shí)近年來,隨著 Android 版本不斷迭代,Google 提供的Android 系統(tǒng)已經(jīng)越來越流暢,目前最新發(fā)布的版本是 Android 8.0 Oreo 。但是在國內(nèi)大部分用戶用的 Android 手機(jī)系是各大廠商定制過的版本,往往不是最新的原生系統(tǒng)內(nèi)核,可能絕大多數(shù)還停留在 Android 5.0 系統(tǒng)上,甚至 Android 6.0 以上所占比例還偏小,更新存在延遲性。
? ? 由于 Android 系統(tǒng)源碼是開放的,每個(gè)人只要遵從相應(yīng)的協(xié)議,就可以對源碼進(jìn)行修改,那么國內(nèi)各個(gè)廠商就把基于 Android 源碼改造成自己對外發(fā)布的系統(tǒng),比如我們熟悉的小米手機(jī) Miui 系統(tǒng)、華為手機(jī) EMUI 系統(tǒng)、Oppo 手機(jī) ColorOS 系統(tǒng)等。由于每個(gè)廠商都修改過 Android 原生系統(tǒng)源碼,這里面就會(huì)引發(fā)一個(gè)問題,那就是著名的Android 碎片化問題,本質(zhì)就是不同 Android 系統(tǒng)的應(yīng)用兼容性不同,達(dá)不到一致性。
? ? 由于存在著各種 Android 碎片化和兼容性問題,導(dǎo)致 Android 開發(fā)者在開發(fā)應(yīng)用時(shí)需要對不同系統(tǒng)進(jìn)行適配,同時(shí)每個(gè) Android 開發(fā)者的開發(fā)水平參差不齊,寫出來的應(yīng)用性能也都存在不同類型的問題,導(dǎo)致用戶在使用過程中用戶體驗(yàn)感受不同,那么有些問題用戶就會(huì)轉(zhuǎn)化為 Android 系統(tǒng)問題,進(jìn)而影響對Android 手機(jī)的評價(jià)。
性能優(yōu)化
今天想說的重點(diǎn)是Android APP 性能優(yōu)化,也就是在開發(fā)應(yīng)用程序時(shí)應(yīng)該注意的點(diǎn)有哪些,如何更好地提高用戶體驗(yàn)。一個(gè)好的應(yīng)用,除了要有吸引人的功能和交互之外,在性能上也應(yīng)該有高的要求,即時(shí)應(yīng)用非常具有特色,在產(chǎn)品前期可能吸引了部分用戶,但是用戶體驗(yàn)不好的話,也會(huì)給產(chǎn)品帶來不好的口碑。
那么一個(gè)好的應(yīng)用應(yīng)該如何定義呢?主要有以下三方面:
? ? ? 業(yè)務(wù)/功能
? ? ? 符合邏輯的交互
? ? ? 優(yōu)秀的性能
眾所周知,Android 系統(tǒng)作為以移動(dòng)設(shè)備為主的操作系統(tǒng),硬件配置是有一定的限制的,雖然配置現(xiàn)在越來越高級,但仍然無法與 PC 相比,在 CPU 和內(nèi)存上使用不合理或者耗費(fèi)資源多時(shí),就會(huì)碰到內(nèi)存不足導(dǎo)致的穩(wěn)定性問題、CPU 消耗太多導(dǎo)致的卡頓問題等。
面對問題時(shí),大家想到的都是聯(lián)系用戶,然后查看日志,但殊不知有關(guān)性能類問題的反饋,原因也非常難找,日志大多用處不大,為何呢?因?yàn)樾阅軉栴}大部分是非必現(xiàn)的問題,問題定位很難復(fù)現(xiàn),而又沒有關(guān)鍵的日志,當(dāng)然就無法找到原因了。這些問題非常影響用戶體驗(yàn)和功能使用,所以了解一些性能優(yōu)化的一些解決方案就顯得很重要了,并在實(shí)際的項(xiàng)目中優(yōu)化我們的應(yīng)用,進(jìn)而提高用戶體驗(yàn)。
四個(gè)方面
可以把用戶體驗(yàn)的性能問題主要總結(jié)為4個(gè)類別:
? ? ? 流暢
? ? ? 穩(wěn)定
? ? ? 省電、省流量
? ? ? 安裝包小
性能問題的主要原因是什么,原因有相同的,也有不同的,但歸根到底,不外乎內(nèi)存使用、代碼效率、合適的策略邏輯、代碼質(zhì)量、安裝包體積這一類問題,整理歸類如下:
從圖中可以看到,打造一個(gè)高質(zhì)量的應(yīng)用應(yīng)該以4個(gè)方向?yàn)槟繕?biāo):快、穩(wěn)、省、小。
快:使用時(shí)避免出現(xiàn)卡頓,響應(yīng)速度快,減少用戶等待的時(shí)間,滿足用戶期望。
穩(wěn):減低 crash 率和 ANR 率,不要在用戶使用過程中崩潰和無響應(yīng)。
?。汗?jié)省流量和耗電,減少用戶使用成本,避免使用時(shí)導(dǎo)致手機(jī)發(fā)燙。
?。喊惭b包小可以降低用戶的安裝成本。
要想達(dá)到這4個(gè)目標(biāo),具體實(shí)現(xiàn)是在右邊框里的問題:卡頓、內(nèi)存使用不合理、代碼質(zhì)量差、代碼邏輯亂、安裝包過大,這些問題也是在開發(fā)過程中碰到最多的問題,在實(shí)現(xiàn)業(yè)務(wù)需求同時(shí),也需要考慮到這點(diǎn),多花時(shí)間去思考,如何避免功能完成后再來做優(yōu)化,不然的話等功能實(shí)現(xiàn)后帶來的維護(hù)成本會(huì)增加。
卡頓優(yōu)化
Android 應(yīng)用啟動(dòng)慢,使用時(shí)經(jīng)??D,是非常影響用戶體驗(yàn)的,應(yīng)該盡量避免出現(xiàn)??D的場景有很多,按場景可以分為4類:UI 繪制、應(yīng)用啟動(dòng)、頁面跳轉(zhuǎn)、事件響應(yīng),如圖:
這4種卡頓場景的根本原因可以分為兩大類:
? ? 界面繪制。
主要原因是繪制的層級深、頁面復(fù)雜、刷新不合理,由于這些原因?qū)е驴D的場景更多出現(xiàn)在 UI 和啟動(dòng)后的初始界面以及跳轉(zhuǎn)到頁面的繪制上。
? ? 數(shù)據(jù)處理。
導(dǎo)致這種卡頓場景的原因是數(shù)據(jù)處理量太大,一般分為三種情況:
一是數(shù)據(jù)在處理 UI 線程,
二是數(shù)據(jù)處理占用 CPU 高,導(dǎo)致主線程拿不到時(shí)間片,
三是內(nèi)存增加導(dǎo)致 GC 頻繁,從而引起卡頓。
引起卡頓的原因很多,但不管怎么樣的原因和場景,最終都是通過設(shè)備屏幕上顯示來達(dá)到用戶,歸根到底就是顯示有問題,所以,要解決卡頓,就要先了解 Android 系統(tǒng)的顯示原理。
Android系統(tǒng)顯示原理
Android 顯示過程可以簡單概括為:Android 應(yīng)用程序把經(jīng)過測量、布局、繪制后的 surface 緩存數(shù)據(jù),通過 SurfaceFlinger 把數(shù)據(jù)渲染到顯示屏幕上, 通過 Android 的刷新機(jī)制來刷新數(shù)據(jù)。也就是說應(yīng)用層負(fù)責(zé)繪制,系統(tǒng)層負(fù)責(zé)渲染,通過進(jìn)程間通信把應(yīng)用層需要繪制的數(shù)據(jù)傳遞到系統(tǒng)層服務(wù),系統(tǒng)層服務(wù)通過刷新機(jī)制把數(shù)據(jù)更新到屏幕上。
我們都知道在 Android 的每個(gè) View 繪制中有三個(gè)核心步驟:Measure、Layout、Draw。具體實(shí)現(xiàn)是從 ViewRootImp 類的performTraversals() 方法開始執(zhí)行,Measure 和 Layout都是通過遞歸來獲取 View 的大小和位置,并且以深度作為優(yōu)先級,可以看出層級越深、元素越多、耗時(shí)也就越長。
真正把需要顯示的數(shù)據(jù)渲染到屏幕上,是通過系統(tǒng)級進(jìn)程中的 SurfaceFlinger 服務(wù)來實(shí)現(xiàn)的,那么這個(gè)SurfaceFlinger 服務(wù)主要做了哪些工作呢?如下:
? ? ?響應(yīng)客戶端事件,創(chuàng)建 Layer 與客戶端的 Surface 建立連接。
? ? ?接收客戶端數(shù)據(jù)及屬性,修改 Layer 屬性,如尺寸、顏色、透明度等。
? ? ?將創(chuàng)建的 Layer 內(nèi)容刷新到屏幕上。
? ? ?維持 Layer 的序列,并對 Layer 最終輸出做出裁剪計(jì)算。
既然是兩個(gè)不同的進(jìn)程,那么肯定是需要一個(gè)跨進(jìn)程的通信機(jī)制來實(shí)現(xiàn)數(shù)據(jù)傳遞,在 Android 顯示系統(tǒng)中,使用了 Android 的匿名共享內(nèi)存:SharedClient,每一個(gè)應(yīng)用和 SurfaceFlinger 之間都會(huì)創(chuàng)建一個(gè)SharedClient ,然后在每個(gè) SharedClient 中,最多可以創(chuàng)建 31 個(gè) SharedBufferStack,每個(gè) Surface 都對應(yīng)一個(gè) SharedBufferStack,也就是一個(gè) Window。
一個(gè) SharedClient 對應(yīng)一個(gè)Android 應(yīng)用程序,而一個(gè) Android 應(yīng)用程序可能包含多個(gè)窗口,即 Surface 。也就是說 SharedClient 包含的是 SharedBufferStack的集合,其中在顯示刷新機(jī)制中用到了雙緩沖和三重緩沖技術(shù)。最后總結(jié)起來顯示整體流程分為三個(gè)模塊:應(yīng)用層繪制到緩存區(qū),SurfaceFlinger 把緩存區(qū)數(shù)據(jù)渲染到屏幕,由于是不同的進(jìn)程,所以使用 Android 的匿名共享內(nèi)存 SharedClient 緩存需要顯示的數(shù)據(jù)來達(dá)到目的。
除此之外,我們還需要一個(gè)名詞:FPS。FPS 表示每秒傳遞的幀數(shù)。在理想情況下,60 FPS 就感覺不到卡,這意味著每個(gè)繪制時(shí)長應(yīng)該在16 ms 以內(nèi)。但是 Android 系統(tǒng)很有可能無法及時(shí)完成那些復(fù)雜的頁面渲染操作。Android 系統(tǒng)每隔 16ms 發(fā)出 VSYNC 信號,觸發(fā)對 UI 進(jìn)行渲染,如果每次渲染都成功,這樣就能夠達(dá)到流暢的畫面所需的 60FPS。如果某個(gè)操作花費(fèi)的時(shí)間是 24ms ,系統(tǒng)在得到 VSYNC 信號時(shí)就無法正常進(jìn)行正常渲染,這樣就發(fā)生了丟幀現(xiàn)象。那么用戶在 32ms 內(nèi)看到的會(huì)是同一幀畫面,這種現(xiàn)象在執(zhí)行動(dòng)畫或滑動(dòng)列表比較常見,還有可能是你的 Layout 太過復(fù)雜,層疊太多的繪制單元,無法在 16ms 完成渲染,最終引起刷新不及時(shí)。
卡頓根本原因
根據(jù)Android 系統(tǒng)顯示原理可以看到,影響繪制的根本原因有以下兩個(gè)方面:
? ?繪制任務(wù)太重,繪制一幀內(nèi)容耗時(shí)太長。
? ? ?主線程太忙,根據(jù)系統(tǒng)傳遞過來的 VSYNC 信號來時(shí)還沒準(zhǔn)備好數(shù)據(jù)導(dǎo)致丟幀。
繪制耗時(shí)太長,有一些工具可以幫助我們定位問題。主線程太忙則需要注意了,主線程關(guān)鍵職責(zé)是處理用戶交互,在屏幕上繪制像素,并進(jìn)行加載顯示相關(guān)的數(shù)據(jù),所以特別需要避免任何主線程的事情,這樣應(yīng)用程序才能保持對用戶操作的即時(shí)響應(yīng)。
總結(jié)起來,主線程主要做以下幾個(gè)方面工作:
? ? ?UI 生命周期控制
? ? ?系統(tǒng)事件處理
? ? ?消息處理
? ? ?界面布局
? ? ?界面繪制
? ? ?界面刷新
除此之外,應(yīng)該盡量避免將其他處理放在主線程中,特別復(fù)雜的數(shù)據(jù)計(jì)算和網(wǎng)絡(luò)請求等。
性能分析工具
性能問題并不容易復(fù)現(xiàn),也不好定位,但是真的碰到問題還是需要去解決的,那么分析問題和確認(rèn)問題是否解決,就需要借助相應(yīng)的的調(diào)試工具,比如查看 Layout 層次的 Hierarchy View、Android 系統(tǒng)上帶的 GPU Profile 工具和靜態(tài)代碼檢查工具 Lint 等,這些工具對性能優(yōu)化起到非常重要的作用,所以要熟悉,知道在什么場景用什么工具來分析。
1,Profile GPU Rendering
在手機(jī)開發(fā)者模式下,有一個(gè)卡頓檢測工具叫做:Profile GPU Rendering,如圖:
它的功能特點(diǎn)如下:
? ? ?一個(gè)圖形監(jiān)測工具,能實(shí)時(shí)反應(yīng)當(dāng)前繪制的耗時(shí)
? ? ?橫軸表示時(shí)間,縱軸表示每一幀的耗時(shí)
? ? ?隨著時(shí)間推移,從左到右的刷新呈現(xiàn)
? ? ?提供一個(gè)標(biāo)準(zhǔn)的耗時(shí),如果高于標(biāo)準(zhǔn)耗時(shí),就表示當(dāng)前這一幀丟失
2,TraceView
TraceView 是 Android SDK 自帶的工具,用來分析函數(shù)調(diào)用過程,可以對 Android 的應(yīng)用程序以及 Framework 層的代碼進(jìn)行性能分析。它是一個(gè)圖形化的工具,最終會(huì)產(chǎn)生一個(gè)圖表,用于對性能分析進(jìn)行說明,可以分析到每一個(gè)方法的執(zhí)行時(shí)間,其中可以統(tǒng)計(jì)出該方法調(diào)用次數(shù)和遞歸次數(shù),實(shí)際時(shí)長等參數(shù)維度,使用非常直觀,分析性能非常方便。
3,Systrace UI 性能分析
Systrace 是 Android 4.1及以上版本提供的性能數(shù)據(jù)采樣和分析工具,它是通過系統(tǒng)的角度來返回一些信息。它可以幫助開發(fā)者收集 Android 關(guān)鍵子系統(tǒng),如 surfaceflinger、WindowManagerService 等 Framework 部分關(guān)鍵模塊、服務(wù)、View系統(tǒng)等運(yùn)行信息,從而幫助開發(fā)者更直觀地分析系統(tǒng)瓶頸,改進(jìn)性能。Systrace 的功能包括跟蹤系統(tǒng)的 I/O 操作、內(nèi)核工作隊(duì)列、CPU 負(fù)載等,在 UI 顯示性能分析上提供很好的數(shù)據(jù),特別是在動(dòng)畫播放不流暢、渲染卡等問題上。
優(yōu)化建議
1,布局優(yōu)化
布局是否合理主要影響的是頁面測量時(shí)間的多少,我們知道一個(gè)頁面的顯示測量和繪制過程都是通過遞歸來完成的,多叉樹遍歷的時(shí)間與樹的高度h有關(guān),其時(shí)間復(fù)雜度 O(h),如果層級太深,每增加一層則會(huì)增加更多的頁面顯示時(shí)間,所以布局的合理性就顯得很重要。
那布局優(yōu)化有哪些方法呢,主要通過減少層級、減少測量和繪制時(shí)間、提高復(fù)用性三個(gè)方面入手??偨Y(jié)如下:
? ? ?減少層級。合理使用 RelativeLayout 和 LinerLayout,合理使用Merge。
? ? ?提高顯示速度。使用 ViewStub,它是一個(gè)看不見的、不占布局位置、占用資源非常小的視圖對象。
? ? ?布局復(fù)用??梢酝ㄟ^ 標(biāo)簽來提高復(fù)用。
? ? ?盡可能少用wrap_content。wrap_content 會(huì)增加布局 measure 時(shí)計(jì)算成本,在已知寬高為固定值時(shí),不用wrap_content 。
? ? ?刪除控件中無用的屬性。
2,避免過度繪制
過度繪制是指在屏幕上的某個(gè)像素在同一幀的時(shí)間內(nèi)被繪制了多次。在多層次重疊的 UI 結(jié)構(gòu)中,如果不可見的 UI 也在做繪制的操作,就會(huì)導(dǎo)致某些像素區(qū)域被繪制了多次,從而浪費(fèi)了多余的 CPU 以及 GPU 資源。
如何避免過度繪制呢,如下:
? ? ?布局上的優(yōu)化。移除 XML 中非必須的背景,移除 Window 默認(rèn)的背景、按需顯示占位背景圖片
? ? ?自定義View優(yōu)化。使用 canvas.clipRect()來幫助系統(tǒng)識(shí)別那些可見的區(qū)域,只有在這個(gè)區(qū)域內(nèi)才會(huì)被繪制。
3,啟動(dòng)優(yōu)化
通過對啟動(dòng)速度的監(jiān)控,發(fā)現(xiàn)影響啟動(dòng)速度的問題所在,優(yōu)化啟動(dòng)邏輯,提高應(yīng)用的啟動(dòng)速度。啟動(dòng)主要完成三件事:UI 布局、繪制和數(shù)據(jù)準(zhǔn)備。因此啟動(dòng)速度優(yōu)化就是需要優(yōu)化這三個(gè)過程:
? ? ?UI 布局。應(yīng)用一般都有閃屏頁,優(yōu)化閃屏頁的 UI 布局,可以通過 Profile GPU Rendering 檢測丟幀情況。
? ? ?啟動(dòng)加載邏輯優(yōu)化??梢圆捎梅植技虞d、異步加載、延期加載策略來提高應(yīng)用啟動(dòng)速度。
? ? ?數(shù)據(jù)準(zhǔn)備。數(shù)據(jù)初始化分析,加載數(shù)據(jù)可以考慮用線程初始化等策略。
4,合理的刷新機(jī)制
在應(yīng)用開發(fā)過程中,因?yàn)閿?shù)據(jù)的變化,需要刷新頁面來展示新的數(shù)據(jù),但頻繁刷新會(huì)增加資源開銷,并且可能導(dǎo)致卡頓發(fā)生,因此,需要一個(gè)合理的刷新機(jī)制來提高整體的 UI 流暢度。合理的刷新需要注意以下幾點(diǎn):
? ? ?盡量減少刷新次數(shù)。
? ? ?盡量避免后臺(tái)有高的 CPU 線程運(yùn)行。
? ? ?縮小刷新區(qū)域。
5,其他
在實(shí)現(xiàn)動(dòng)畫效果時(shí),需要根據(jù)不同場景選擇合適的動(dòng)畫框架來實(shí)現(xiàn)。有些情況下,可以用硬件加速方式來提供流暢度。
評論
查看更多