演講實錄——Android性能優化指南 [復制鏈接]

2019-4-26 10:24
九霄逆鱗 閱讀:410 評論:0 贊:0
Tag:  

本文來自2019安卓巴士開發者大會現場實錄,由于錄入匆忙,內容可能存在偏差,歡迎大家掃描文末二維碼查看現場實錄視頻和下載大會完整PPT。作者:邱炬


風雨再大不過是學習路上的點綴,雷聲再響也阻止不了開發者渴望成長的步伐!

——美圖高級安卓開發工程師  邱炬


各位開發者大家下午好,我今天給大家演講的題目是《Android性能優化指南》。
其實這個主題特別大,花1、2天講解都講不完,我今天概括的講一下。屏幕上看我們有90多個PPT,所以可能我的語速會比較快,大家想要拍照的話手速就要快一點。
我主要從四個方面來演講,第一個是為什么需要性能優化,第二個是什么是性能優化,第三個性能優化應該怎么做,第四個是一些注意事項。
現在我先說一下為什么要做性能優化,這個非常簡單,我們都是給產品增加功能,這個時候就需要APP運行,我們設備商的迭代就非常重要。我們發現幾年之前的手機運行和現在的手機差得比較多,我們的軟件不斷的上升,功能在不斷的迭代,所以對于硬件的需求更大。還有我們的安卓碎片化也非常嚴重,同時我們想要有一些人口福利的話,這個本身也是我們需要優化的一個過程,輕微的問題就是延遲,嚴重的就是無法響應,所以我要說的第一個性能優化是可以提升用戶的存留的。

我這里說兩個例子,Pinterest他們做了一個調研,他們性能優化了之后用戶的注冊率增加了40%,他們做的一個縮短時間的簡單改進用戶數就有了一個很大的提升。還有一個是BBC他們發現網站的加載時間每增加1秒就會失去一些用戶,所以他們也做了性能優化。
所以我們說產品的目的是讓用戶與我們的內容進行互動,也就是消費數據,所以我們說的第二個點就是提升用戶的轉化率。簡單來說就是商業化,你有了用戶是首先,有了用戶之后我們說一個例子,就是Mobify,他們首頁加載的施加每減少100毫秒,他們發現基于會話的轉化率增加1.11%,年均收入都有一個比較大的增長。
還有一個谷歌的分析平臺,就是你的網頁的加載時間在5秒之內可以完成這個收益是非常顯著的,留住用戶是第一個,轉化率是第二個,第三個就是提升用戶的體驗。這里我們我也說一個例子,這個例子當中我們的右邊有一個用戶的簡單信息界面,我們想要加載數據的話,有兩個地方可以優化,一個是策略型,一個是效率型。第一個策略型就是增加用戶的主觀感受,它退出你這個頁面的可能性就減少了。第三個是效率型,我們加載一個圖片的時候,我們把一個大圖換成一個小圖片,這個也是一個優化。

所以我們總結一下為什么要性能優化,就是吸引用戶提升用戶的轉化率賺更多的錢,實際開發當中我們要注意用戶的主觀感受和實際體驗,自然而然產品的價值就上升了。
還有第二個點什么是性能優化,這里我總結一下就是增加我們的負載能力,從而提升我們的性能讓程序更快的運行。這個地方有四個點,這四個點都是和用戶息息相關的,用戶做一個操作的時候,我們希望用戶是100毫秒完成,并且動畫要流暢,我們設置動畫或滾動時,在10毫秒以內生成,還有最大程度增加主線程的空閑時間,并且持續的吸引用戶,在1000毫秒以內呈現交互內容。
這個是一個延遲和用戶反映之間的簡單的圖,大家可以看一下,比較關注的一個是16毫,這個是我們動畫的點,100毫秒以下是我們的用戶響應的操作,就是用戶打游戲的時候不應該大于100毫秒,否則用戶的體驗就會非常差。100—300用戶就可以感受到延遲了,再多到了1秒的話用戶的注意力會轉移,10秒以上用戶就會拋棄你。我用過一些政府的軟件,他們的驗證碼我點了一下獲取,結果這個驗證碼是需要等一分鐘的,我已經收到了驗證碼,這個時候我要等一分鐘,這個時候的體驗就非常不好。

還有一個我們的性能優化是需要有一定的指標的,首先要提出問題,找到問題,并且對問題進行分類。第二個我們修改之前要測量系統上面的問題,也就是找到我們的瓶頸,之后我們修復我們的瓶頸,修復好了之后我們做二次的測量,確定我們的修復是不是有效的。這個是一個大家需要的過程,其實開發過程當中我建議大家一定按照這樣的流程做一做,有這樣的數據支撐之后一定會更好。
第三個我們說一下性能優化怎么做,我說到這個題的時候大家有沒有一些標簽,比如你腦子里面有沒有線程或者GPU這些關鍵詞,有的話還是有一定的方向的,如果沒有的話我們往下看。我們看性能優化就是把用戶的哭臉變成笑臉,這個過程當中我們就要找到用戶的痛點,最關鍵的就是卡頓不跟手,我們說BUG這個東西開發當中就安卓直接解決要,我們說到卡頓和不跟手,最主要的是用戶基礎上的設備信息,所以我們說要發揮設備上的一些東西,這些地方都可以進行優化。但是優化不是說就是把一個地方降低,不是我降低內存和CPU就是降低了,這個就是一個性能優化,所以說它是一個均衡,不是一個簡簡單單的降低就好了。
性能優化有5個方向,我們說到最后一個是電池方向,這個部分我們不做講解,因為關于網絡部分也給大家說一下,我們主要講前面的顯然還有計算以及內存方面的優化。

網絡的這里很簡單,里要配合服務器來改,因為一些東西和你的業務信息息息相關。
之后我們說到渲染,我們說屏幕關鍵的指標是1S內屏幕的刷新次數也就是每秒顯示的幀數,我們看一下這個電影的攝像是記錄的是一段時間的信息,而不是一瞬。我們說快門是有一個快門速度的,你打開的時間長一點可能就是看到的是另外一個圖片,再長一點就是另外一個圖片,看見的是同一幀,但是用戶感覺是在動,所以這個就是動態模糊。其實在我們的一些游戲上也有一些應用,這里并不是說我們打開的時間越長越好,打開時間太長可能就會出現這種流光。

旁邊這里是一個游戲,我們可以看到兩幀其實都是完全靜態的,里面沒有模糊的東西,你看見的模糊就是因為它的渲染不體好,所以這個草看不清楚,旁邊這個人在運動的時候,除了人之外,這個人還算清晰,大部分電影當中人也是模糊的,電影也在發展,我們都想縮短這個時間拿到更多的信息。
這個模糊不是簡單的高斯模糊,這個圖片我們高斯模糊之后就是這樣的,我們動態模糊之后要獲取相關的信息,我們看這三個圖片,中間這個圖本來是動的,但是沒有動,這個地方就是說靜態的時候,你的界面沒有東西,第二個動畫就是交互的時候這個也是一個波動狀態,這里并不是說一定要達到60才是一個關鍵點。
所以我們總結一下動態模糊可以在低FPS時幫助大腦處理運動信息,所以我們需要更高的FPS展示,游戲這個東西也說過了,FPS作為屏幕流暢度的參考就不是一個絕對值,更多的還有一些其他方面的指標來衡量。我們說一下進行屏幕繪制的時候干了一些什么,我們需要很多幀的合成,進行數字的更新操作之后我們再繪制,如果其中某一個幀的時候,你簡單的構建的時候你花了24毫秒,不是說這個時候你立刻可以停下刷新,這個時候你還是要等,這里因為有一個垂直同步的存在,這個是因為我們屏幕本身有一個刷新率,我們取的時候應該有一個實際去取。所以這個就是所謂的丟幀,說了丟幀我們說你把一個東西想要繪制在屏幕上的時候,不是簡單的畫上去,屏幕其實本身來說是由很多的像素組成的,歸根到底屏幕還是很多的顆粒組成的,并沒有想象當中得那么平滑。

我們把這樣的一個三角形會知道我們后面的方格的屏幕上,我們繪制之后其實是這樣的,并不是我們一個簡單的三角形,我們方格的密度小一點就是左邊的這個,密度高一點就是右邊這個。我們看到整個的過程就設計我們CPU完成整個過程的運算,要把矢量的圖片換成像素的信息,這個就是我們說的柵格化。

我們要顯示這個鯊魚的圖片的話,首先要有一個鯊魚的輪廓,也就是我們的矢量圖,之后我們要渲染,它下面的紋理就是我們的一些顏色,這里需要準備這兩個東西,之后交給我們的東西做合成,之后把它會知道我們的屏幕上,這個時候CPU本身是可以來做的,并且可以降低我們的效率,這里還可以通訊,而且里面還幫助我們維持了緩存什么的。

前面說了CPU和GPU分別要干的事情,就拿CPU來說要做的就是輸入,你輸入的可能是你的對象,不管是什么對象都要有一個解析的過程,之后做測量和布局,這個東西是最終給GPU的東西,這個過程一個是頁面深度,非常深的情況下測量的時候耗費的時間會比較多,還有一個點是我們有一些控件可能失效了,這個時候也會進行額外的收集。所以我們說這里還是要使用一些工具,這個東西其實也可以看到它的層級,只是無法說我們測量之后的時間,這個東西其實我們可以通過后面的CPU耗時來統計。
解決方法就是布局簡單化,對于我們GPU來說,它做的事情就是我們說的柵格化,你做要柵格化有多個矢量圖在一起的時候,就有一個問題出現了,我們找到這個問題之后通過我們手機上的開發者調試工具就可以知道他里面是不是過度的繪制,知道了之后我們進行優化。無非就是更改表格,另外把我們的自定義的控件合并的時候進行剪切,并且讓他重復的位置避免重復。

這個東西右邊的截圖是我們Android6.0以上的截圖,對于Android6.0來說它的信息會多一些,這里不過多講解。并且這里我們也可以知道我們每一次的繪制里面做了這些操作都是可以看到的,再有這里就是我們的OVERDRAW,我們要做的就是一個轉化,并且我們要通過代碼把背景移除掉,再有就是你的一些以加載圖片的時候會有這樣的一個圖片,之后你加載之后把這個圖片移除掉,這個時候最簡單的就是把最后面的去掉。

還有自定義控件當中進行一個檢查,之后再進行一個剪切。這個是我們的一個按紐,在左面是一個簡單的按紐,右邊是這個按紐的縱向圖,每一層里面都有背景,我們要做的就是把后面的移除,讓我們的按紐繪制的時候減少層次。

這個是谷歌的一個圖片,在你的自定義的卡片布局當中,上面遮擋下面的時候,會在一個區域繪制兩次,所以這個時候需要我們自己完成。再說性能優化的第二個點就是計算部分,看一個簡單的例子,我們點擊屏幕上面的按紐,要想得到右面的這個效果,其實它首先要控件點擊在屏幕上,這個是系統集成控制的,這個里面會經過一系列的事件,之后可以拿到屏幕輸入的事件,之后把它派發出去傳遞到我們當前的機制,之后我們進行解析之后進行調度,調度之后我們還需要層層的分析找到控件,這個時候有很多的派發和操作,所以主線程是非常繁忙的,這里很榮幸就可以導致你這里卡頓的情況。
前面我說到如果你卡頓了5秒以上就會出現一個ANR,其實這個東西一般出現的有四個點,簡單來說你進入一個界面的時候,你在界面上進行線程的操作的時候,這個時候你不用你的手指觸摸的話,你的程序還可以運作,但是你觸摸了之后這個屏幕就會出現一個ANR,出現這個問題其實有幾個點。一個是計算耗時,還有一個是等待,還有一個是得不到CPU的資源。要解決的話一個是讓主線程等待,還有就是主線程在等待的時候,我們看到主線程在等待什么東西,下面會有兩個線程有兩個思索。

計算耗時的排查方案有以下幾個點,還有一個就是通過我們的事件打點,這個比較是傻瓜式的,但是對于我們的測量是比較有用的,它影響的是所有的方法的耗時,所以它是均等的,它的占比是不會變的,并不會影響太多。
我們看一下IOS這個里面的工具,主窗口有五個地方需要給大家講講,第一個地方就是我們的記錄配置,新版里面有五個配置,一個是簡單的,一個是默認的,它有隨機的堆棧的信息,知道我們當前的線程在干什么事情之后進行分析,這里有一個問題,就是當他這次取代到下次取代這個間隔有一個線程進入了堆棧,這個時候就不會捕獲,這樣我們的性能點分析的時候就會出現這樣的一個方式,你的手機的負擔就會比較大。

重點我們看一下第四個點,我們的CPU的核心數正在干什么,第三個點是我們的一些事件,你可以通過第一個點快速的選擇,第四個點就是我們CPU的時間線,第五個點就是我們當前線程的活躍點,如果出現了一個色塊,那么活躍這里也有幾個顏色,不同的顏色也有不同的活躍狀態。
如果我們選擇一個地方點擊第二個地方錄制之后,你看到的界面就是類似這樣的,這樣的東西當中,我們主要關注的也就這些地方,第一個是時間范圍,這里你可以進行時間選擇,你得到的分析就更加精確,第二個點是我們的時間戳,第三個是跟蹤窗格,第四個是分析類型可以進行切換,后面有我們的調用圖表還有我們的火焰表,第五個是我們的函數時間類型,這里有兩個,一個是我們的真實方法耗時,還有一個CPU對線程的耗時,最大的差異是等待的話,這個線程沒有進行資源利用,你選擇的時候這個分析數據其實是沒有的。

看一下我們這個簡單的例子,這個里面啟動了一個線程,里面做了一個簡單的等待,之后做了一個非常大數據的數據打印,這個東西在我們系統跑下來是什么樣?我們跑下來之后可以看到這樣的一個效果,我有兩個紅框的部分,這個部分是真實的事件消耗的地方,之所以我們做CPU的消耗是因為我們點擊屏幕的時候是有系統的進程通知的,所以說這個線程本身會占用一定的時間,還有我們主線程把基本上的控件進行刷新也會耗費一定的時間。
還有我們說的這個方法里面大家可以知道我們當前在這個點這個地方CPU正在消耗的時候我們正在干什么事情,如果我們切換到我們下面這個線程的時候,我們再把它現在到我們線程實際消耗的地方,我們可以顯現這里有非常多的消耗,我們剛剛說了旁邊有時間的選擇方式,如果我們選擇第一種方式,那么就是這樣的,如果我們選擇第二個方式,我們就可以知道當前的線程沒有做太多的內部計算的操作,其實它的大部分是都是在等待,所以它是沒有統計信息的。

之后我們到第四個我們可以看到方法的耗時的排列,我們從多到少做一個排序,你點擊進去這個方法的話,你要看子方法的耗時,通過這樣我們就可以做一個性能優化。下面是幾個不同的方式,我們所看到的內容是不一樣的,給大家看一下這個Call Chart,你看到的這個圖和下面的兩個圖,這里調用B方法的時候耗費的時間是比B方法少的,我們看D方法內部有C和B,C這邊我們有一個倡議就是C和B加起來還沒有把D占滿,我們發現每一個方法耗費時間比較長的話我們會看到,但是有的耗費時間比較短,但是多次占用的話,我們把這個問題轉換過去其實是有一定的關聯的,簡單來說就是把同樣調度順序的方法進行了匯總,這邊來說我們的B方法是從A到D到B的,所以這里的調度順序是一樣的,我們可以合并起來作為總時間。所以我們說這個條形的柱狀圖自動的加長,我們的C1和C3也是一樣的,這樣的方式可以幫助我們直接排查,可以讓我們知道哪個方法的綜合調度比較好,這個過程當中我們可以看到B123耗時是比較長的,并且調用的子方法和消耗辦法都是一樣的。
我們這些C函數作為調度的時候都可以給予幫助,下次給大家講一下這個。我說一下Trace  System  Calls這個方式,這個模式得到的數據比前面要多得多,一個是我們的幀,一個是我們的CPU,CPU我分析的時候,我發現我們前面可能沒有做系統的調度,這里僅僅是從4到7的進行調度,我們看到我們選擇的主線程分布到這4個CPU里面可以知道整個CPU里面的調度過程,這個時候多并發的過程我們可以知道CPU的情況。

選擇不同的線程的時候,CPU其實是會變化的,它標識的是我們當前CPU的情況,這個東西是非常重要的,與此同時我們剛剛說的這個圖我們選擇主線程的時候,我們可以看到底部,這里最上面的大箭頭是有一個54.8毫秒的現象,這個丟幀就是我們選擇了這個區間的時候下面就選擇了我們的線程,之后進行了輸出,之后我們看到下面得做的一個事情就是當前CPU做了一個額外的操作,之后做了一個解析布局的操作,這里地方其實也是一個可以優化的點。
我們說一下常見的影響性能的操作,我們看到對于我們APP打下會有一個增加,比如你做了一些設計模式和策略模式,它這里可以提供很多的便利,這個情況下你也可以使用一下,但是不要太多。還有一個就是我們的StringBuilder替換為+運算符,其實這里可以做一個時間的簡單測量。
還有一些比如你盡可能使用直接類型,避免使用BigInteger。這里說完了我們說一下內存優化,這里不說原理了,不說我們內存是怎么分配的,我們說一下影響內存的東西就是我們的GC,一個是我們的內存抖動,還有一個是我們的內存泄露,第一個內存抖動就是短時內大量的對象進出方法棧,還有一個內存修樓是不再需要的對象被意外持有,第三個影響就是大對象引起的GC操作。

我們看一下內存分析的東西也是一樣,我們看這個模型來說這里有七個點,第一個點我們可以知道當前的內存里面的對象可以進行強制回收,可以回收的話就沒有問題,不可以回收的話有沒有內存泄露就取決于我們的實際情況。還有一個是我們的捕獲的類型以及后面的456都沒有太多的信息。
我們看一下我們的內存抖動,出現內存抖動的話,你看到的圖其實和網絡上的不一樣,這個圖不是峰值,但是出現了很多的金色的標記,有一個虛線一直往上,說明我們創造了非常多的對象,并且在不斷的上升,而且我們的內存上沒有一個太大的幅度,但是你頻繁的回收就出現了內存的抖動。

排查這里我們拿到這個信息先選擇抖動的區間,之后選擇分配對象最多的點,再點一下第四個這里可能需要你點擊不同的地方,我們一般是點第一個就好了,這里我們點第四個位置,點了之后我就可以看到當前對象引用的最小路徑,我們看到我們的線程是這里被引用的,所以我們可以知道這個地方會有一個抖動的地方。
這個代碼很簡單,就是這個線程里面做循環,之后不斷的做拼接,這個就是拼好了之后用完了就丟了,所以這里有一個內存的抖動,表現為一個大局的情況,我們的內存是有不同的很多小的波浪,我們的CPU也有很多不同的小的波浪,上面也有一些波浪的出現,這個是我們的方法里面做了一些耗時操作。

這里說一下內存泄露,表現的是內存不斷的上漲,但是可能這里回收了一部分,但是沒有所有都回收。對象的數量在不斷的上漲,可以還原的地方就是內存的抖動,下面這里很多可以回收的標就存在,我們說最上面的箭頭也有一個Java占用的內存,這個內存是不斷的上升的話這個時候就有一個內存泄露的存在。
我們拿到一個區間之后進行排查,之后要多次操作你的界面,如果這個幅度平穩下來了,我們最好選擇平穩的區間,你選擇的上升的區間就不利于你的分析,如果你已經平穩了,你選擇好了之后我們看第二個點。第三個點這里當前的對象數量有多少,還有我們選的后面的排序方式我們排序一下,當前占用的內存大小來確定當前的排序。

我們到了這個地方某個地方都是一樣的,之后我們看一下我們引用的鏈,拿了之后就可以確定問題,這個是我們排查上面的一些簡單步驟,其實我們這個案例是一個短時泄露,之后我們進行其實是可以內存泄露的,第三個位置這里我們也可以看到我們的一個總數和占有都得到了一個想象。
這個期間我們看一下總得概覽,大概就是這樣的一個情況,這個是我們剛剛的例子,也就是我們把創建的組件全部存起來,為什么說這個是一個短時內存泄露,我們當前的堆棧引起的話沒有辦法得到計算,但是線程的棧之后也可以得到回收,所以我們說內存泄露有短時的和永久的。
內存處理思路我們對于懷疑的地方進行壓測,把多余的一些內存排除掉,有必要的情況下可以錄制一段內存的記錄。還有有必要的時候我們可以打出多份內存的快照。最后說一下我們性能優化的準則,其實第一個就是不要過早的優化你的程序,不是說你開發的過程當中不優化你的程序,你開發的過程當中有一些耗時可以及時優化,因為有可能你第三天的需求有可能把你前面的推翻。

第二個良好的結構是優于一切的,你APP這里是可以避免非常多的問題。第三個是采用高效的算法。第四個是使用工具找到性能瓶頸,切忌過度優化,有可能你優化的過度了,第二天就立刻被推翻了,第四點我建議大家深入的理解,我看似把性能優化了,但是可能帶來了更多的隱患。
還有后續這里很多東西都沒有給大家講,我們前面講的就是一個簡單的指標,這個東西其實大家后面可以關注一下我們的文章之類的,我們也會進行一個推送,計算這里我們也有非常多的內容可以和大家講解,大家也可以自己拓展。關于內存也是一樣,還有我們的網絡電池這里也是一樣的,我們這里還有各種工具的對比,本來我這里也有很多的工具對比,我大概有20多種工具,這里的這些工具大家可以看一下,這里的工具我都沒有放PPT,我是直接截圖的,所以沒有顏色。
后面我會把這些整理出來,希望大家喜歡,最后謝謝大家,感謝大家的聆聽,謝謝。

作者Github: https://github.com/qiujuer 
慕課網講師頁介紹:https://www.imooc.com/t/1339513

現場PPT分享:關注【安卓巴士Android開發者門戶】公眾號,后臺回復“420”獲取講師完整PPT。
大會現場視頻小程序:



我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(0)
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

两码中特期期