讓你的app體驗更絲滑的11種方法!衝擊手機應用榜單Top3指日可待

来源:https://www.cnblogs.com/qcloud1001/archive/2018/08/23/9525078.html
-Advertisement-
Play Games

歡迎大家前往 "騰訊雲+社區" ,獲取更多騰訊海量技術實踐乾貨哦~ 本文由 "WeTest質量開放平臺團隊 " 發表於 "雲+社區專欄" 一款app除了要有令人驚嘆的功能和令人髮指交互之外,在性能上也應該追求絲滑的要求,這樣才能更好地提高用戶體驗。 以下是本人在工作中對經歷過的性能優化的一些總結,依 ...


歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~

本文由WeTest質量開放平臺團隊發表於雲+社區專欄

一款app除了要有令人驚嘆的功能和令人髮指交互之外,在性能上也應該追求絲滑的要求,這樣才能更好地提高用戶體驗。


以下是本人在工作中對經歷過的性能優化的一些總結,依據故事的發展路線,將其分為了5個部分,分別是:常見的性能問題;產生性能問題的一些可能原因;解決性能問題的套路;代碼建議及潛在性能問題排查項。

img1.png

如看不清大圖,下文會有拆解

一 首先,我們先瞭解一下都有哪些性能問題


img2.png

1、記憶體泄露。

通俗來講,記憶體泄露不僅僅會造成應用記憶體占用過大,還會導致應用卡頓,造成不好的用戶體驗,至於,為什麼一個“小小的”記憶體泄露會造成應用卡頓,我不得不拿這幅圖來說說話了。

img3.png

沒錯,這就是Android開發童鞋需要瞭解的Generational Heap Memory模型,這裡我們只關心當對象在Young Generation中存活了一段時間之後,如果沒被幹掉,那麼會被移動到Old Generation中,同理,最後會移動到Permanent Generation中。那麼用腳想一想就知道,如果記憶體泄露了,那麼,抱歉,你那塊記憶體隨時間推移自然而然將進入Permanent Generation中,然鵝,記憶體不是白菜,想要多少就有多少,這裡,因為沙盒機制的原因,分配給你應用的記憶體當然是有那麼一個極限值的,你不能逾越(有人笑了,不是有large heap麽,當然我也笑了,我並沒有看到這貨被宗師android玩家青睞過),好了,你那塊造成泄露記憶體的對象占著茅坑不拉屎,剩下來可以供其他對象發揮的記憶體空間就少了;打個比方,舞臺小了,演員要登臺表演,沒有多餘空間,他就只能等待其他演員下來他才能表演啊,這等待的時間,是沒法連續表演的,所以就卡了嘛。

2、頻繁GC

呵呵,頻繁GC會造成卡頓,想必你經過上面的洗禮,已經知道了為什麼,不錯,當然也是因為“舞臺空間不足,新的演員上臺表演需要先讓表演完的下來”。那麼造成這種現象的原因是什麼呢?

a、記憶體泄露,好的,你懂了,不用講了,這個必須有可能會造成。

b、大量對象短時間被創建,又在短時間內“需要”被釋放,註意這裡的需要,其實是不得不,為什麼,同樣是因為“舞臺空間不夠了”,舉個例子,在onDraw中new 對象,因為onDraw大約16ms會執行一次(wait,你能否確定一下,什麼是大約16ms,對不起,不能,掉幀了就不是,哪怕掉那麼一點點)。腦補一下,每秒中創建大約60個對象,嗯,騷年,你以為Young Generation是白菜麽,想拿多少就拿多少,對不起,這裡是限量的,這裡用完了,在來申請,我就得去回收一些回來,我回收總得耗時間吧,耗時間,好吧,onDraw 等著等著就錯過了下一個16ms的執行了,如是,用戶看起來就卡了。

3、耗電問題

km上有一個問題很尖銳,說是微視看小視頻看一會手機就會發燙,所以,用戶一直就很關註耗電問題,不過不好意思,我們的app至今還沒有遇到過嚴重的耗電問題,雖然沒有遇到比較嚴重的耗電問題,不代表就不需要去瞭解這樣的問題的解決辦法,我總結有:

a、沒有什麼特別重要的信息,比如,錢到賬,電話來了,100元實打實無門檻代金券方法,等等,請不要打擾用戶,不要頻繁喚醒用戶,否則,結果只能是卸載,或者關閉一切通知。

b、適當的做本地緩存,避免頻繁請求網路數據,這裡,說起來容易,做起來並非三刀兩斧就能搞定,要配合良好的緩存策略,區分哪些是一段時間不會變更的,哪些是絕對不能緩存的很重要。

c、對某些執行時間較長的同步操作在用戶充電且有wifi的時候在做,除非用戶強制同步..等等,就不扯太多,因為後面還有很多內容。

4、OOM問題

呵呵,這個問題,想必經過前面1、2的洗禮,你應該已經明白這個什麼原因導致的,你可以想想一下"舞臺上將要上的一個演員是一個巨大胖子,即便不表演的演員都下來了,他還是擠不上去,怎麼辦,演砸了,還能怎麼辦,直接崩潰,散場!"造成這個問題的原因,可能有,(呵呵,保險起見,只能說可能,分析的時候可以從這裡出發)

a、記憶體泄露了,想必你會心一笑。

b、大量不可見的對象占據記憶體,這個其實,很常見,只是大家可能一直不太關心罷了,比如,請求介面返回了列表有100項數據,每項數據比如有100個欄位,其中你用戶展示數據的只有10幾個而已,但是,你解析的時候,剩下的99個不知不覺吃了你的記憶體,當,有個胖子要記憶體時,呵呵,嗝屁了。

c、還有一種很常見的場景是一個頁面多圖的場景,明明每個圖只需要載入一個100_100的,你卻使用原始尺寸(1080_1980)or更大,而且你一下子還載入個幾十張,扛得住麽?所以瞭解一下inSampleSize,或者,如果圖片歸你們上傳管理,你可以藉助萬象優圖,他為你做了剪切好不同尺寸的圖片,這樣省得你在客戶端做圖片縮放了。

二 以上瞭解了一些性能問題,這裡,簡單的串一串導致這些性能問題的原因


img4.png

1、人為在ui線程中做了輕微的耗時操作,導致ui線程卡頓

嗯,很多小伙伴不以為然,以為在onCreate中讀一下pref算什麼,解析下json數據算得了什麼,可實際情況是並不是這樣的,正確的做法是,將這些操作使用非同步封裝起來,小伙伴可以瞭解一下rxjava,現在最新版本已經是rxjava2了,如果不清楚使用方式,可以Google一下。

2、layout過於複雜,無法在16ms完成渲染

這個很多小伙伴深有體會了,這裡簡單的瞭解下,我們先簡單的把渲染大概分為"layout","measure""draw"這麼幾個階段,當然你不要以為實際情況也是如此,好,層級複雜,layout,measure可能就用到了不該用的時間,自然而然,留給draw的時間就可能不夠了,自然而然就悲劇了。那麼以前給出的很多建議是,使用RelativeLayout替換LinearLayout,說是可以減少佈局層次,然鵝,現在請不要在建議別人使用RelativeLayout,因為ConstraintLayout才是一個更高性能的消滅佈局層級的神器。ConstraintLayout 基於Cassowary演算法,而Cassowary演算法的優勢是在於解決線性方程時有極高的效率,事實證明,線性方程組是非常適合用於定義用戶界面元素的參數。由於人們對圖形的敏感度非常高,所以UI的渲染速度顯得非常重要。因此在2016年,iOS和Android都基於Cassowary演算法來研發了屬於自己的佈局系統,這裡是ConstraintLayout與傳統佈局RelativeLayout,LinearLayout實現時的性能對比,不過這裡是老外的測試數據,原文可以參考這裡。demo中也提供了測試的方法,感興趣的小伙伴可以嘗試一下咯。

img5.png

測量/佈局(單位:毫秒,100 幀的平均值)

3、同一時間執行的動畫過多,導致CPU或者GPU負載過重

這裡主要是因為動畫一般會頻繁變更view的屬性,導致displayList失效,而需要重新創建一個新的displayList,如果動畫過多,這個開銷可想而知,如果你想瞭解得更加詳細,推薦看這篇咯,知識點在第5節那裡。

4、view過度繪製的問題。

view過度繪製的問題可以說是我們在寫佈局的時候遇到的一個最常見的問題之一,可以說寫著寫著一不留神就寫出了一個過度繪製,通常發生在一個嵌套的viewgroup中,比如你給他設置了一個不必要的背景。這方面問題的排查不太難,我們可以通過手機設置裡面的開發者選項,打開Show GPU Overdraw的選項,輕鬆發現這些問題,然後儘量往藍色靠近。

img6.png

5、gc過多的問題,這裡就不在贅述了,上面已經講的非常直接了。

6、資源載入導致執行緩慢。

有些時候避免不要載入一些資源,這裡有兩種解決的辦法,使用的場景也不相同。

a、預載入,即還沒有來到路徑之前,就提前載入好,誒,好像x5內核就是醬紫哦。

b、實在是要等到用到的時候載入,請給一個進度條,不要讓用戶乾等著,也不知道什麼時候結束而造成不好的用戶體驗。

7、工作線程優先順序設置不對,導致和ui線程搶占cpu時間。

使用Rxjava的小伙伴要註意這點,設置任務的執行線程可能會對你的性能產生較大的影響,沒有使用的小伙伴也不能太過大意。

8、靜態變數。

嘿嘿,大家一定有過在application中設置靜態變數的經歷,遙想當年,為了越過Intent只能傳遞1M以下數據的坑,我在application中設置了一個靜態變數,用於兩個activity“傳遞(共用)數據”,然而,一步小心,數據中,有著前一個activity的尾巴,因此泄露了。不光是這樣的例子,隨便舉幾個:

a、你用靜態集合保存過數據吧?

b、某某單例的Manger,比如管理AudioManger遇到過吧?

三 既然遇到問題分析也有了,那麼接下來,自然而然是如何使用各種刀棒棍劍來解決這些問題了


img7.png

1、GPU過度繪製,定位過度繪製區域

這裡直接在開發者選項,打開Show GPU Overdraw,就可以看到效果,輕鬆發現哪塊需要優化,那麼具體如何去優化

a、減少佈局層級,上面有提到過,使用ConstraintLayout替換傳統的佈局方式。如果你對ConstraintLayout不瞭解,沒有關係,這篇文章教你15分鐘瞭解如何使用ConstraintLayout。

b、檢查是否有多餘的背景色設置,我們通常會犯一些低級錯誤--對被覆蓋的父view設置背景,多數情況下這些背景是沒有必要的。

2、主線程耗時操作排查。

a、開啟strictmode,這樣一來,主線程的耗時操作都將以告警的形式呈現到logcat當中。

b、直接對懷疑的對象加@DebugLog,查看方法執行耗時。DebugLog註解需要引入插件hugo,這個是Android之神JakeWharton的早期作品,對於監控函數執行時間非常方便,直接在函數上加入註解就可以實現,但是有一個缺點,就是JakeWharton發佈的最後一個版本沒有支持release版本用空方法替代監控代碼,因此,我這裡發佈了一個到公司的maven倉庫,引用的方式和官網類似,只不過,地址是:'com.tencent.tip:hugo-plugin:2.0.0-SNAPSHOT'。

3、對於measure,layout耗時過多的問題

一般這類問題是優於佈局過於複雜的原因導致,現在因為有ConstraintLayout,所以,強烈建議使用ConstraintLayout減少佈局層級,問題一般得以解決,如果發現還存在性能問題,可以使用traceView觀察方法耗時,來定位下具體原因。

4、leakcany

這個是記憶體泄露監測的銀彈,大家應該都使用過,需要提醒一下的是,要註意

dependencies {

debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'

releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'

}

引入方式,releaseImplementation保證在發佈包中移除監控代碼,否則,他自生不停的catch記憶體快照,本身也影響性能。

5、onDraw裡面寫代碼需要註意

onDraw優於大概每16ms都會被執行一次,因此本身就相當於一個forloop,如果你在裡面new對象的話,不知不覺中就滿足了短時間內大量對象創建並釋放,於是頻繁GC就發生了,嗯,記憶體抖動,於是,卡了。因此,正確的做法是將對象放在外面new出來。

6、json反序列化問題

json反序列化是指將json字元串轉變為對象,這裡如果數據量比較多,特別是有相當多的string的時候,解析起來不僅耗時,而且還很吃記憶體。解決的方式是:

a、精簡欄位,與後臺協商,相關介面剔除不必要的欄位。保證最小可用原則。

b、使用流解析,之前我考慮過json解析優化,在Stack Overflow上搜索到這個。於是瞭解到Gson.fromJson是可以這樣玩的,可以提升25%的解析效率。

img8.png

7、viewStub&merge的使用。

這裡merge和viewStub想必是大家非常瞭解的兩個佈局組件了,對於只有在某些條件下才展示出來的組件,建議使用viewStub包裹起來,同樣的道理,include 某佈局如果其根佈局和引入他的父佈局一致,建議使用merge包裹起來,如果你擔心preview效果問題,這裡完全沒有必要,因為你可以

tools:showIn=""屬性,這樣就可以正常展示preview了。

8、載入優化

這裡並沒有過多的技術點在裡面,無非就是將耗時的操作封裝到非同步中去了,但是,有一點不得不提的是,要註意多進程的問題,如果你的應用是多進程,你應該認識到你的application的oncreate方法會被執行多次,你一定不希望資源載入多次吧,於是你只在主進程載入,如是有些坑就出現了,有可能其他進程需要那某份資源,然後他這個進程缺沒有載入相應的資源,然後就嗝屁了。

9、刷新優化。

這點在我之前的文章中有提到過,這裡舉兩個例子吧。

a、對於列表的中的item的操作,比如對item點贊,此時不應該讓整個列表刷新,而是應該只刷新這個item,相比對於熟練使用recyclerView的你,應該明白如何操作了,不懂請看這裡,你將會明白什麼叫做recyclerView的局部刷新。

b、對於較為複雜的頁面,個人建議不要寫在一個activity中,建議使用幾個fragment進行組裝,這樣一來,module的變更可以只刷新某一個具體的fragment,而不用整個頁面都走刷新邏輯。但是問題來了,fragment之間如何共用數據呢?好,看我怎麼操作。

img9.png

Activity將數據這部分抽象成一個LiveData,交個LiveDataManger數據進行管理,然後各個Fragment通過Activity的這個context從LiveDataManger中拿到LiveData,進行操作,通知activity數據變更等等。哈哈,你沒有看錯,這個確實和Google的那個LiveData有點像,當然,如果你想使用Google的那個,也自然沒問題,只不過,這個是簡化版的。項目的引入

'com.tencent.tip:simple_live_data:1.0.1-SNAPSHOT'

10、動畫優化

這裡主要是想說使用硬體加速來做優化,不過要註意,動畫做完之後,關閉硬體加速,因為開啟硬體加速本身就是一種消耗。下麵有一幅圖,第二幅對比第一幅是說開啟硬體加速和沒開啟的時候做動畫的效果對比,可以看到開啟後的渲染速度明顯快不少,開啟硬體加速就一定萬事大吉麽?第三幅圖實際上就說明,如果你的這個view不斷的失效的話,也會出現性能問題,第三圖中可以看到藍色的部曲線圖有了一定的起色,這說明,displaylist不斷的失效並重現創建,如果你想瞭解的更加詳細,可以查看這裡

img10.png

// Set the layer type to hardwaremyView.setLayerType(View.LAYER_TYPE_HARDWARE, null);// Setup the animationObjectAnimator animator = ObjectAnimator.ofFloat(myView,View.TRANSLATION_X, 150);// Add a listener that does cleanupanimator.addListener(new AnimatorListenerAdapter() {undefined @Override public void onAnimationEnd(Animator animation) { myView.setLayerType(View.LAYER_TYPE_NONE, null); } });

11耗電優化

這裡僅僅只是建議;

a、在定位精度要求不高的情況下,使用wifi或移動網路進行定位,沒有必要開啟GPS定位。

b、先驗證網路的可用性,在發送網路請求,比如,當用戶處於2G狀態下,而此時的操作是查看一張大圖,下載下來可能都200多K甚至更大,我們沒必要去發送這個請求,讓用戶一直等待那個菊花吧。

四 接下來的一些內容就比較輕鬆了,是關於一些代碼的建議


img11.png

這裡不一一細講了,僅僅挑標記的部分說下。

pb->model這裡的優化就不在贅述,前面有講如何優化。

然後建議使用SparseArray代替HashMap,這裡是Google建議的,因為SparseArray比HashMap更省記憶體,在某些條件下性能更好,主要是因為它避免了對key的自動裝箱比如(int轉為Integer類型),它內部則是通過兩個數組來進行數據存儲的,一個存儲key,另外一個存儲value,為了優化性能,它內部對數據還採取了壓縮的方式來表示稀疏數組的數據,從而節約記憶體空間。

不到不得已,不要使用wrap_content,,推薦使用match_parent,或者固定尺寸,配合gravity="center",哈哈,你應該懂了的。

那麼為什麼說這樣會比較好。

因為 在測量過程中,match_parent和固定寬高度對應EXACTLY ,而wrap_content對應AT_MOST,這兩者對比AT_MOST耗時較多。

五 總結


這是以上關於我在工作中遇到的性能問題的及處理的一些總結,性能優化設計的方方面面實在是太多太多,本文不可能將全部的性能問題全部總結的清清楚楚,或許還多多少少存在一些紕漏之處,有不對的地方歡迎指出補充。


參考資料

http://developers.googleblog.cn/2017/09/constraintlayout.html等,具體詳見原文

問答
如何發佈節點應用程式?
相關閱讀
游戲人工智慧 讀書筆記 (二) 游戲人工智慧簡史
游戲人工智慧 讀書筆記(一)前言與介紹
低於0.01%的極致Crash率是怎麼做到的?
雲學院 · 課程推薦 | 知乎KOL,與你分享機器學習中如何做選擇

此文已由作者授權騰訊雲+社區發佈,原文請點擊

搜索關註公眾號「雲加社區」,第一時間獲取技術乾貨,關註後回覆1024 送你一份技術課程大禮包!

海量技術實踐經驗,盡在雲加社區


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 文章來源:公眾號-智能化IT系統。 回歸模型有多種,一般在數據分析中用的比較常用的有線性回歸和邏輯回歸。其描述的是一組因變數和自變數之間的關係,通過特定的方程來模擬。這麼做的目的也是為了預測,但有時也不是全部為了預測,只是為瞭解釋一種現象,因果關係。 還是按照老風格,不說空泛的概念,以實際的案例出發 ...
  • 測試環境:Idea+Windows10 準備工作: <1>、打開本地 C:\Windows\System32\drivers\etc(系統預設)下名為hosts的系統文件,如果提示當前用戶沒有許可權打開文件;第一種方法是將hosts文件拖到桌面進行配置後再拖回原處;第二種一勞永逸的方法是修改當前用戶對 ...
  • 文章來源:公眾號-智能化IT系統。 貝葉斯的原理類似於概率反轉,通過先驗概率推導出後驗概率。其公式如下: 在大數據分析中,該定理可以很好的做推導預測,很多電商以及用戶取向可以參照此方式,從已有數據推導出未知數據,以歸類做後續操作。 例如,在一個購房機構的網站,已有8個客戶,信息如下: 這時來了一個新 ...
  • 幫助類: using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; using MySq... ...
  • 1.前言 react-natvie中文網地址:https://reactnative.cn/ 現在前端的開發環境基本都需要先搭建環境.react-natvie環境搭建跟著官網的文檔流程走就行了。但是有些是沒有必要的。 2.搭建環境 2.1選擇平臺 這裡需要註意的就是windows的電腦只能開發and ...
  • 首先,為什麼我們要定義一個新類呢?按照我的理解,就是為了抽象出來一個新的東西(也就是類),用來存儲更多的數據變數和方法,一切類都直接或間接繼承與NSObject。 在類的頭文件里我們可以定義成員變數、屬性變數、和方法,在方法里又分為實例方法和類方法。 1.成員變數 成員變數可以以三個關鍵詞來修飾,即 ...
  • 程式中資料庫文件路徑在/data/data/<package name>/databases/ adb shell 進入adb命令行 ls 列出當前文件夾下所有文件 假設有一個名為angle.db的資料庫 下麵通過這個資料庫說明資料庫的操作方式 進入資料庫 sqlite3 angle.db 查看所有 ...
  • 解決方案類似: Android項目實戰(四十):Andoird 7.0+ 安裝APK適配 解決方法: 一、在AndroidManifest.xml 文件中添加 四大組件之一的 <provider> 註意這裡的 android :authorities 屬性的值 中的 com.xxx.xxxx 是你的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...