A. android流暢度評估及卡頓優化
Google定義:界面呈現是指從應用生成幀並將其顯示在屏幕上的動作。要確保用戶能夠流暢地與應用互動,應用呈現每幀的時間不應超過16ms,以達到每秒60幀的呈現速度(為什麼是60fps?)。
如果應用存在界面呈現緩慢的問題,系統會不得不跳過一些幀,這會導致用戶感覺應用不流暢,我們將這種情況稱為卡頓。
來源於: Google Android的為什麼是60fps?
16ms意味著1000/60hz,相當於60fps。這是因為人眼與大腦之間的協作無法感知超過60fps的畫面更新。12fps大概類似手動快速翻動書籍的幀率, 這明顯是可以感知到不夠順滑的。24fps使得人眼感知的是連續線性的運動,這其實是歸功於運動模糊的效果。 24fps是電影膠圈通常使用的幀率,因為這個幀率已經足夠支撐大部分電影畫面需要表達的內容,同時能夠最大的減少費用支出。 但是低於30fps是 無法順暢表現絢麗的畫面內容的,此時就需要用到60fps來達到想要的效果,超過60fps就沒有必要了。如果我們的應用沒有在16ms內完成屏幕刷新的全部邏輯操作,就會發生卡頓。
首先要了解Android顯示1幀圖像,所經歷的完整過程。
如圖所示,屏幕顯示1幀圖像需要經歷5個步驟:
常見的丟幀情況: 渲染期間可能出現的情況,渲染大於16ms和小於16ms的情況:
上圖中應該繪制 4 幀數據 , 但是實際上只繪制了 3 幀 , 實際幀率少了一幀
判斷APP是否出現卡頓,我們從通用應用和游戲兩個緯度的代表公司標准來看,即Google的Android vitals性能指標和地球第一游戲大廠騰訊的PrefDog性能指標。
以Google Vitals的卡頓描述為准,即呈現速度緩慢和幀凍結兩個維度判斷:
PerfDog Jank計算方法:
幀率FPS高並不能反映流暢或不卡頓。比如:FPS為50幀,前200ms渲染一幀,後800ms渲染49幀,雖然幀率50,但依然覺得非常卡頓。同時幀率FPS低,並不代表卡頓,比如無卡頓時均勻FPS為15幀。所以平均幀率FPS與卡頓無任何直接關系)
當了解卡頓的標准以及渲染原理之後,可以得出結論,只有丟幀情況才能准確判斷是否卡頓。
mpsys 是一種在設備上運行並轉儲需要關注的系統服務狀態信息的 Android 工具。通過向 mpsys 傳遞 gfxinfo 命令,可以提供 logcat 格式的輸出,其中包含與錄制階段發生的動畫幀相關的性能信息。
藉助 Android 6.0(API 級別 23),該命令可將在整個進程生命周期中收集的幀數據的聚合分析輸出到 logcat。例如:
這些總體統計信息可以得到期間的FPS、Jank比例、各類渲染異常數量統計。
命令 adb shell mpsys gfxinfo <PACKAGE_NAME> framestats 可提供最近120個幀中,渲染各階段帶有納秒時間戳的幀時間信息。
關鍵參數說明:
通過gfxinfo輸出的幀信息,通過定時reset和列印幀信息,可以得到FPS(幀數/列印間隔時間)、丟幀比例((janky_frames / total_frames_rendered)*100 %)、是否有幀凍結(幀耗時>700ms)。
根據第2部分的通用應用卡頓標准,可以通過丟幀比例和幀凍結數量,准確判斷當前場景是否卡頓。並且通過定時截圖,還可以根據截圖定位卡頓的具體場景。
如上圖所示,利用gfxinfo開發的檢查卡頓的小工具,圖中參數和卡頓說明如下:
根據上面對gfxinfo的幀信息解析,可以准確計算出每一幀的耗時。從而可以開發出滿足騰訊PerfDog中關於普通卡頓和嚴重卡頓的判斷。
依賴定時截圖,即可准確定位卡頓場景。如下圖所示(此處以PerfDog截圖示例):
通過第3部分的卡頓評估方法,我們可以定位到卡頓場景,但是如何定位到具體卡頓原因呢。
首先了解卡頓問題定位工具,然後再了解常見的卡頓原因,即可通過復現卡頓場景的同時,用工具去定位具體卡頓問題。
重點就是,充分利用gfxinfo輸出的幀信息,對卡頓問題進行分類。
了解了高效定位卡頓的方法和卡頓問題定位工具,再熟悉一下常見的卡頓原因,可以更熟練的定位和優化卡頓。
SurfaceFlinger 負責 Surface 的合成,一旦 SurfaceFlinger 主線程調用超時,就會產生掉幀。
SurfaceFlinger 主線程耗時會也會導致 hwc service 和 crtc 不能及時完成,也會阻塞應用的 binder 調用,如 dequeueBuffer、queueBuffer 等。
後台進程活動太多,會導致系統非常繁忙,cpu io memory 等資源都會被佔用,這時候很容易出現卡頓問題,這也是系統這邊經常會碰到的問題。
mpsys cpuinfo 可以查看一段時間內 cpu 的使用情況:
當線程為 Runnable 狀態的時候,調度器如果遲遲不能對齊進行調度,那麼就會產生長時間的 Runnable 線程狀態,導致錯過 Vsync 而產生流暢性問題。
system_server 的 AMS 鎖和 WMS 鎖 , 在系統異常的情況下 , 會變得非常嚴重 , 如下圖所示 , 許多系統的關鍵任務都被阻塞 , 等待鎖的釋放 , 這時候如果有 App 發來的 Binder 請求帶鎖 , 那麼也會進入等待狀態 , 這時候 App 就會產生性能問題 ; 如果此時做 Window 動畫 , 那麼 system_server 的這些鎖也會導致窗口動畫卡頓。
Android P 修改了 Layer 的計算方法 , 把這部分放到了 SurfaceFlinger 主線程去執行, 如果後台 Layer 過多,就會導致 SurfaceFlinger 在執行 rebuildLayerStacks 的時候耗時 , 導致 SurfaceFlinger 主線程執行時間過長。
主線程執行 Input Animation Measure Layout Draw decodeBitmap 等操作超時都會導致卡頓 。
Activity resume 的時候, 與 AMS 通信要持有 AMS 鎖, 這時候如果碰到後台比較繁忙的時候, 等鎖操作就會比較耗時, 導致部分場景因為這個卡頓, 比如多任務手勢操作。
應用裡面涉及到 WebView 的時候, 如果頁面比較復雜, WebView 的性能就會比較差, 從而造成卡頓。
如果屏幕幀率和系統的 fps 不相符 , 那麼有可能會導致畫面不是那麼順暢. 比如使用 90 Hz 的屏幕搭配 60 fps 的動畫。
由上面的分析可知對象分配、垃圾回收(GC)、線程調度以及Binder調用 是Android系統中常見的卡頓原因,因此卡頓優化主要以下幾種方法,更多的要結合具體的應用來進行:
在計算機和通信領域,幀是一個包括「幀同步串列」的數字數據傳輸單元或數字數據包。
在視頻領域,電影、電視、數字視頻等可視為隨時間連續變換的許多張畫面,其中幀是指每一張畫面。
B. 打穿越火線蘋果的60幀和安卓的60幀哪個好一點
蘋果的60幀更好一些。
首先,iPhone已經擁有規格較高的屏幕,就拿去年發布的iPhone 12來說,全系列標配OLED三星訂制屏幕。雖然沒有高刷新率加持,但無論是畫面觀感還是色彩還原程度,都已經達到業界頂尖水準,蘋果用不著通過提高刷新率的方式來給自己加碼。另一方面,iPhone擁有基於底層語言優化的iOS,再配合A系列晶元,得以突破算力瓶頸。所以即便iPhone沒有高刷新率,但它的操作體驗本就很流暢。對於蘋果來說,與其花心思研究屏幕,倒不如在系統更新方面下功夫。
蘋果公司(Apple Inc. )是美國一家高科技公司。由史蒂夫·喬布斯、斯蒂夫·蓋瑞·沃茲尼亞克和羅納德·傑拉爾德·韋恩(Ron Wayne)等人於1976年4月1日創立,並命名為美國蘋果電腦公司(Apple Computer Inc.),2007年1月9日更名為蘋果公司,總部位於加利福尼亞州的庫比蒂諾。
C. 如何提升安卓系統的流暢度
可進行如下操作:
安卓刷機
系統這一塊絕對是有著舉足輕重的地位,一款好的系統能讓安卓手機脫胎換骨。而從Android 4.1開始,Android的流暢性可以說有了質的飛躍。Android 4.1的觸控感覺非常好,這主要歸功於Android 4.1的幀速度提高到了60fps,而且在觸摸延遲上有更加優秀的表現。因此只要情況允許,建議所有Android手機都刷到4.1以上,這種體驗絕對是以往使用4.0甚至2.3系統都不可想像的。當然,對於大部分Android手機來說,4.1還是遙不可及,只有通過第三方ROM才能達到品嘗「果凍豆」的目的,這里就要特別注意第三方ROM的穩定性問題。
如果不能刷Android 4.1,那還可以選擇一些第三方ROM例如原生系統AOKP或者CM系列。由於系統非常精簡,這些第三方ROM也會帶來流暢度的提升,當然要放棄的是官方ROM的各種自帶軟體和UI,這就要看用戶的取捨了。
更換內核
Android手機的內核(Kernel)對手機流暢性也是有很大的影響,內核直接影響CPU的運行效率、頻率變化。說到刷內核就不能不提超頻,一些第三方內核支持CPU的超頻,CPU頻率提高了流暢度當然會有變化,當然這里也要特別注意溫度和電壓的控制。
已經有提及過,很多手機默認是標准模式甚至是省電模式,這對性能是有不少影響的,因此建議不是有特別需求還是調至性能模式(位置:系統設置,因不同手機而異)。
關閉動畫特效
這是一個Android 4.0才開始有的設置選項,Android 4.0有兩項(窗口動畫縮放以及過渡動畫縮放),Android 4.1增加了動畫程序時長調整。有人說Android的動畫比較卡,沒有iPhone順滑,有這選項可好,你說動畫不順嘛,我關掉還不行嗎?關閉了這些以後會感覺反應迅速了很多,但是犧牲了一定的視覺感受(位置:設置→開發人員選項)。
不保留活動
這個選項即把Android相對iPhone的其中一個很大的優勢舍棄了,也就是我們常說的「多任務」,Android現在的高端機動不動就四核、2G RAM,如果只跑一個軟體,可想而知流暢度會非常高,但是這里並不建議使用這種方法提升流暢度,沒有多任務的Android更像一隻三腳貓,如果只是體驗一下那種感覺也無妨(位置:設置→開發人員選項)。
藉助第三方軟體優化
實際上很多Android卡頓的罪魁禍首就是系統的啟動器,現在一些品牌的手機啟動器做得越來越炫麗,也越來越復雜,當然代價就是佔用RAM和ROM更多,如果不是對這方面特別有要求,完全可以替換一些第三方的啟動器,例如Apex、NOVA等,它們帶來的流暢度提升也是非常明顯的(這里使用NOVA作介紹)。
卸載系統自帶程序
現在越來越多官方系統自帶很多惱人的程序,而且不能卸載,這些軟體往往都會開機自啟動,對系統流暢度影響比較大,但是要刪除這些自帶軟體需要獲取root許可權。通用一些第三方ROM也會有自帶垃圾軟體問題,而大部分第三方ROM都自帶root,所以這個相對好解決。這里刪除程序也要特別注意,不要錯刪一些系統軟體,否則後果很嚴重(這里使用的是「力卓工具箱」)。
建議:卸載一些不常用的桌面插件、系統強制安裝的第三方軟體等
管理開機自啟動項
這方面在之前的省電專題中也有提到過,減少開機自啟動的軟體,除了能節省電量之外,當然還能提升手機的反應速度,當然這里也不能一下把所有軟體都關閉,關閉一些不需要自啟動的第三方軟體就可以達到目的了,如果不小心把系統本身的程序禁用了就會比較麻煩(這里使用的是「力卓工具箱」)。
END
D. 針對Android的性能優化集中哪些方面
一、概要:
本文主要以Android的渲染機制、UI優化、多線程的處理、緩存處理、電量優化以及代碼規范等幾方面來簡述Android的性能優化
二、渲染機制的優化:
大多數用戶感知到的卡頓等性能問題的最主要根源都是因為渲染性能。
Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染, 如果每次渲染都成功,這樣就能夠達到流暢的畫面所需要的60fps,為了能夠實現60fps,這意味著程序的大多數操作都必須在16ms內完成。
*關於JobScheler的更多知識可以參考http://hukai.me/android-training-course-in-chinese/background-jobs/scheling/index.html
七、代碼規范
1)for loop中不要聲明臨時變數,不到萬不得已不要在裡面寫try catch。
2)明白垃圾回收機制,避免頻繁GC,內存泄漏,OOM(有機會專門說)
3)合理使用數據類型,StringBuilder代替String,少用枚舉enum,少用父類聲明(List,Map)
4)如果你有頻繁的new線程,那最好通過線程池去execute它們,減少線程創建開銷。
5)你要知道單例的好處,並正確的使用它。
6)多用常量,少用顯式的"action_key",並維護一個常量類,別重復聲明這些常量。
7)如果可以,至少要弄懂設計模式中的策略模式,組合模式,裝飾模式,工廠模式,觀察者模式,這些能幫助你合理的解耦,即使需求頻繁變更,你也不用害怕牽一發而動全身。需求變更不可怕,可怕的是沒有在寫代碼之前做合理的設計。
8)View中設置緩存屬性.setDrawingCache為true.
9)cursor的使用。不過要注意管理好cursor,不要每次打開關閉cursor.因為打開關閉Cursor非常耗時。Cursor.require用於刷cursor.
10)採用SurfaceView在子線程刷新UI,避免手勢的處理和繪制在同一UI線程(普通View都這樣做)
11)採用JNI,將耗時間的處理放到c/c++層來處理
12)有些能用文件操作的,盡量採用文件操作,文件操作的速度比資料庫的操作要快10倍左右
13)懶載入和緩存機制。訪問網路的耗時操作啟動一個新線程來做,而不要再UI線程來做
14)如果方法用不到成員變數,可以把方法申明為static,性能會提高到15%到20%
15)避免使用getter/setter存取field,可以把field申明為public,直接訪問
16)私有內部類要訪問外部類的field或方法時,其成員變數不要用private,因為在編譯時會生成setter/getter,影響性能。可以把外部類的field或方法聲明為包訪問許可權
17)合理利用浮點數,浮點數比整型慢兩倍
18)針對ListView的性能優化,ListView的背景色與cacheColorHint設置相同顏色,可以提高滑動時的渲染性能。ListView中getView是性能是關鍵,這里要盡可能的優化。
getView方法中要重用view;getView方法中不能做復雜的邏輯計算,特別是資料庫操作,否則會嚴重影響滑動時的性能
19)不用new關鍵詞創建類的實例,用new關鍵詞創建類的實例時,構造函數鏈中的所有構造函數都會被自動調用。但如果一個對象實現了Cloneable介面,我們可以調用它的clone()方法。
clone()方法不會調用任何類構造函數。在使用設計模式(Design Pattern)的場合,如果用Factory模式創建對象,則改用clone()方法創建新的對象實例非常簡單。例如,下面是Factory模式的一個典型實現:
20)public static Credit getNewCredit() {
return new Credit();
}
改進後的代碼使用clone()方法,如下所示:
private static Credit BaseCredit = new Credit();
public static Credit getNewCredit() {
return (Credit) BaseCredit.clone();
}
上面的思路對於數組處理同樣很有用。
21)乘法和除法
考慮下面的代碼:
for (val = 0; val < 100000; val +=5) { alterX = val * 8; myResult = val * 2; }
用移位操作替代乘法操作可以極大地提高性能。下面是修改後的代碼:
for (val = 0; val < 100000; val += 5) { alterX = val << 3; myResult = val << 1; }
22)ViewPager同時緩存page數最好為最小值3,如果過多,那麼第一次顯示時,ViewPager所初始化的pager就會很多,這樣pager累積渲染耗時就會增多,看起來就卡。
23)每個pager應該只在顯示時才載入網路或資料庫(UserVisibleHint=true),最好不要預載入數據,以免造成浪費
24)提高下載速度:要控制好同時下載的最大任務數,同時給InputStream再包一層緩沖流會更快(如BufferedInputStream)
25)提供載入速度:讓服務端提供不同解析度的圖片才是最好的解決方案。還有合理使用內存緩存,使用開源的框架
引用:Android性能優化的淺談