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

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

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

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


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

img
1.png

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

一 首先,咱們先了解一下都有哪些性能問題數組


img
2.png

一、內存泄露。緩存

通俗來說,內存泄露不只僅會形成應用內存佔用過大,還會致使應用卡頓,形成很差的用戶體驗,至於,爲何一個「小小的」內存泄露會形成應用卡頓,我不得不拿這幅圖來講說話了。性能優化

img
3.png

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

二、頻繁GCapp

呵呵,頻繁GC會形成卡頓,想必你通過上面的洗禮,已經知道了爲何,不錯,固然也是由於「舞臺空間不足,新的演員上臺表演須要先讓表演完的下來」。那麼形成這種現象的緣由是什麼呢?

a、內存泄露,好的,你懂了,不用講了,這個必須有可能會形成。

b、大量對象短期被建立,又在短期內「須要」被釋放,注意這裏的須要,實際上是不得不,爲何,一樣是由於「舞臺空間不夠了」,舉個例子,在onDraw中new 對象,由於onDraw大約16ms會執行一次(wait,你可否肯定一下,什麼是大約16ms,對不起,不能,掉幀了就不是,哪怕掉那麼一點點)。腦補一下,每秒中建立大約60個對象,嗯,騷年,你覺得Young Generation是白菜麼,想拿多少就拿多少,對不起,這裏是限量的,這裏用完了,在來申請,我就得去回收一些回來,我回收總得耗時間吧,耗時間,好吧,onDraw 等着等着就錯過了下一個16ms的執行了,如是,用戶看起來就卡了。

三、耗電問題

km上有一個問題很尖銳,說是微視看小視頻看一會手機就會發燙,因此,用戶一直就很關注耗電問題,不過很差意思,咱們的app至今尚未遇到過嚴重的耗電問題,雖然沒有遇到比較嚴重的耗電問題,不表明就不須要去了解這樣的問題的解決辦法,我總結有:

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

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

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

四、OOM問題

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

a、內存泄露了,想必你會心一笑。

b、大量不可見的對象佔據內存,這個其實,很常見,只是你們可能一直不太關心罷了,好比,請求接口返回了列表有100項數據,每項數據好比有100個字段,其中你用戶展現數據的只有10幾個而已,可是,你解析的時候,剩下的99個不知不覺吃了你的內存,當,有個胖子要內存時,呵呵,嗝屁了。

c、還有一種很常見的場景是一個頁面多圖的場景,明明每一個圖只須要加載一個100_100的,你卻使用原始尺寸(1080_1980)or更大,並且你一會兒還加載個幾十張,扛得住麼?因此瞭解一下inSampleSize,或者,若是圖片歸大家上傳管理,你能夠藉助萬象優圖,他爲你作了剪切好不一樣尺寸的圖片,這樣免得你在客戶端作圖片縮放了。

二 以上了解了一些性能問題,這裏,簡單的串一串致使這些性能問題的緣由


img
4.png

一、人爲在ui線程中作了輕微的耗時操做,致使ui線程卡頓

嗯,不少小夥伴不覺得然,覺得在onCreate中讀一下pref算什麼,解析下json數據算得了什麼,可實際狀況是並非這樣的,正確的作法是,將這些操做使用異步封裝起來,小夥伴能夠了解一下rxjava,如今最新版本已是rxjava2了,若是不清楚使用方式,能夠Google一下。

二、layout過於複雜,沒法在16ms完成渲染

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

img
5.png

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

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

這裏主要是由於動畫通常會頻繁變動view的屬性,致使displayList失效,而須要從新建立一個新的displayList,若是動畫過多,這個開銷可想而知,若是你想了解得更加詳細,推薦看這篇咯,知識點在第5節那裏。

四、view過分繪製的問題。

view過分繪製的問題能夠說是咱們在寫佈局的時候遇到的一個最多見的問題之一,能夠說寫着寫着一不留神就寫出了一個過分繪製,一般發生在一個嵌套的viewgroup中,好比你給他設置了一個沒必要要的背景。這方面問題的排查不太難,咱們能夠經過手機設置裏面的開發者選項,打開Show GPU Overdraw的選項,輕鬆發現這些問題,而後儘可能往藍色靠近。

img
6.png

五、gc過多的問題,這裏就不在贅述了,上面已經講的很是直接了。

六、資源加載致使執行緩慢。

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

a、預加載,即尚未來到路徑以前,就提早加載好,誒,好像x5內核就是醬紫哦。

b、實在是要等到用到的時候加載,請給一個進度條,不要讓用戶乾等着,也不知道何時結束而形成很差的用戶體驗。

七、工做線程優先級設置不對,致使和ui線程搶佔cpu時間。

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

八、靜態變量。

嘿嘿,你們必定有過在application中設置靜態變量的經歷,遙想當年,爲了越過Intent只能傳遞1M如下數據的坑,我在application中設置了一個靜態變量,用於兩個activity「傳遞(共享)數據」,然而,一步當心,數據中,有着前一個activity的尾巴,所以泄露了。不光是這樣的例子,隨便舉幾個:

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

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

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


img
7.png

一、GPU過分繪製,定位過分繪製區域

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

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

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

二、主線程耗時操做排查。

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

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

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

通常這類問題是優於佈局過於複雜的緣由致使,如今由於有ConstraintLayout,因此,強烈建議使用ConstraintLayout減小布局層級,問題通常得以解決,若是發現還存在性能問題,可使用traceView觀察方法耗時,來定位下具體緣由。

四、leakcany

這個是內存泄露監測的銀彈,你們應該都使用過,須要提醒一下的是,要注意

dependencies {

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

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

}

引入方式,releaseImplementation保證在發佈包中移除監控代碼,不然,他自生不停的catch內存快照,自己也影響性能。

五、onDraw裏面寫代碼須要注意

onDraw優於大概每16ms都會被執行一次,所以自己就至關於一個forloop,若是你在裏面new對象的話,不知不覺中就知足了短期內大量對象建立並釋放,因而頻繁GC就發生了,嗯,內存抖動,因而,卡了。所以,正確的作法是將對象放在外面new出來。

六、json反序列化問題

json反序列化是指將json字符串轉變爲對象,這裏若是數據量比較多,特別是有至關多的string的時候,解析起來不只耗時,並且還很吃內存。解決的方式是:

a、精簡字段,與後臺協商,相關接口剔除沒必要要的字段。保證最小可用原則。

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

img
8.png

七、viewStub&merge的使用。

這裏merge和viewStub想必是你們很是瞭解的兩個佈局組件了,對於只有在某些條件下才展現出來的組件,建議使用viewStub包裹起來,一樣的道理,include 某佈局若是其根佈局和引入他的父佈局一致,建議使用merge包裹起來,若是你擔憂preview效果問題,這裏徹底沒有必要,由於你能夠

tools:showIn=""屬性,這樣就能夠正常展現preview了。

八、加載優化

這裏並無過多的技術點在裏面,無非就是將耗時的操做封裝到異步中去了,可是,有一點不得不提的是,要注意多進程的問題,若是你的應用是多進程,你應該認識到你的application的oncreate方法會被執行屢次,你必定不但願資源加載屢次吧,因而你只在主進程加載,如是有些坑就出現了,有可能其餘進程須要那某份資源,而後他這個進程缺沒有加載相應的資源,而後就嗝屁了。

九、刷新優化。

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

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

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

img
9.png

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

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

十、動畫優化

這裏主要是想說使用硬件加速來作優化,不過要注意,動畫作完以後,關閉硬件加速,由於開啓硬件加速自己就是一種消耗。下面有一幅圖,第二幅對比第一幅是說開啓硬件加速和沒開啓的時候作動畫的效果對比,能夠看到開啓後的渲染速度明顯快很多,開啓硬件加速就必定萬事大吉麼?第三幅圖實際上就說明,若是你的這個view不斷的失效的話,也會出現性能問題,第三圖中能夠看到藍色的部曲線圖有了必定的轉機,這說明,displaylist不斷的失效並重現建立,若是你想了解的更加詳細,能夠查看這裏

img
10.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甚至更大,咱們不必去發送這個請求,讓用戶一直等待那個菊花吧。

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


img
11.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耗時較多。

五 總結


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


參考資料

developers.googleblog.cn/2017/09/con…等,具體詳見原文

問答

如何發佈節點應用程序?

相關閱讀

遊戲人工智能 讀書筆記 (二) 遊戲人工智能簡史

遊戲人工智能 讀書筆記(一)前言與介紹

低於0.01%的極致Crash率是怎麼作到的?

雲學院 · 課程推薦 | 知乎KOL,與你分享機器學習中如何作選擇

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

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

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

相關文章
相關標籤/搜索