Android性能優(yōu)化 - 卡頓和布局優(yōu)化

  • 布局可以說是APP最重要的一項(xiàng)了,用戶感知極強(qiáng)甜无,無論你的代碼寫的如何扛点,用戶也不知道,用戶只能看到和操作APP岂丘,更漂亮合理的布局陵究,更流暢的體驗(yàn)才是好APP。
  • 比如微信奥帘,操作起來卡铜邮,用戶只會覺得是手機(jī)不行,而不會是微信不行寨蹋,但其他APP卡松蒜,用戶就覺得是APP不行,而不是手機(jī)不行已旧。┓( ′?` )┏
  • Android性能優(yōu)化 - 啟動速度優(yōu)化 也可一起學(xué)習(xí)秸苗。

1.卡頓分析

1.1 刷新率

  • 大多數(shù)用戶感知到的卡頓等性能問題的最主要根源都是因?yàn)殇秩拘阅堋脑O(shè)計(jì)師的角度运褪,他們希望App能夠有更多的動畫难述,圖片等時(shí)尚元素來實(shí)現(xiàn)流暢的用戶體驗(yàn)萤晴。但是Android系統(tǒng)很有可能無法及時(shí)完成那些復(fù)雜的界面渲染操作。Android系統(tǒng)每隔16ms發(fā)出VSYNC信號胁后,觸發(fā)對UI進(jìn)行渲染店读,如果每次渲染都成功,這樣就能夠達(dá)到流暢的畫面所需要的60fps攀芯,為了能夠?qū)崿F(xiàn)60fps屯断,這意味著程序的大多數(shù)操作都必須在16ms內(nèi)完成。
  • 上面那段話就是侣诺,APP做的越來越炫酷殖演,動畫和視頻一大堆,啟動就要顯示視頻廣告年鸳,這些對于旗艦Android機(jī)是無壓力的趴久,但對于老手機(jī),或者是入門手機(jī)搔确,復(fù)雜的頁面彼棍,計(jì)算量大,CPU膳算、GPU處理不過來座硕,也就無法流暢顯示了。


    16ms刷新
  • 如果你的某個(gè)操作花費(fèi)時(shí)間是24ms涕蜂,系統(tǒng)在得到VSYNC信號的時(shí)候就無法進(jìn)行正常渲染华匾,這樣就發(fā)生了丟幀現(xiàn)象。那么用戶在32ms內(nèi)看到的會是同一幀畫面机隙。


    24ms刷新

1.2 PerfDog

騰訊PerfDog,性能監(jiān)控

  • 下圖蜘拉,在PerfDog,使用華為P30Pro有鹿,查看微博的刷新情況诸尽,靜態(tài)的微博內(nèi)容在不滑動的時(shí)候,刷新率就是0fps印颤,快速滑動時(shí)您机,刷新率在60fps左右,還能查看CPU和內(nèi)存是使用情況年局。


    微博
  • 下圖际看,而在微博播放視頻時(shí),刷新率一直就是60fps左右了矢否。


    微博視頻
  • 下圖仲闽,普通的APP都基本能達(dá)到60fps,相機(jī)就不是了僵朗,相機(jī)拍照穩(wěn)定在30fps,而自拍時(shí)赖欣,開啟美顏屑彻,降到24fps了,看來相機(jī)加AI美顏是比較吃性能的顶吮。

  • 小知識社牲,電影或者是網(wǎng)上看的視頻一般是24幀/秒的速度播放的,即可以省性能悴了,效果也不錯(cuò)搏恤,索尼A7M3相機(jī)可以錄制120幀的慢動作,可以做4倍或者5倍升格湃交。


    相機(jī)

1.3 CPU Profile

2.布局優(yōu)化

2.1 過度繪制

  • Overdraw(過度繪制)描述的是屏幕上的某個(gè)像素在同一幀的時(shí)間內(nèi)被繪制了多次息罗。在多層次的UI結(jié)構(gòu)里面,如果不可見的UI也在做繪制的操作才沧,這就會導(dǎo)致某些像素區(qū)域被繪制了多次迈喉。這就浪費(fèi)大量的CPU以及GPU資源。

  • 藍(lán)色綠色是比較好的情況糜工,紅色就是層級較多了弊添,為了實(shí)現(xiàn)好看的效果录淡,就會套多層布局捌木,過度繪制的多了消耗性能,對與入門機(jī)就會卡頓了嫉戚。


    過度繪制
  • 手機(jī)進(jìn)入開發(fā)人員選項(xiàng)刨裆,調(diào)試GPU過度繪制,打開顯示過度繪制區(qū)域彬檀。

  • 貝殼APP的布局大多是藍(lán)色綠色的帆啃,說明他們APP就沒什么過度繪制的情況,非常好窍帝。


    貝殼
  • 到下面的列表就會有過度繪制的情況努潘,但區(qū)域不大,只有內(nèi)容的部分坤学。


    在這里插入圖片描述
  • 開發(fā)人員選項(xiàng)疯坤,顯示布局邊界,可以看到貝殼的布局層級確實(shí)不多深浮,也非常的清晰工整压怠。


    貝殼
  • 微博APP的過度繪制區(qū)域基本占滿整個(gè)屏幕了,除了微博還有微信淘寶等列表APP也是大多數(shù)紅色的飞苇,原因可能是列表類的APP菌瘫,除了父布局蜗顽,里面還要套RecyclerView,再套itemview雨让,無法避免的過度繪制雇盖;但整個(gè)item都過度繪制了,貝殼就會比較好一些宫患。


    微博
  • 微博的布局看起來就會復(fù)雜一些了刊懈。


    在這里插入圖片描述

2.2 解決過度繪制

  • 1.上面的微博跟貝殼比較,微博的item是有過度繪制的情況娃闲,那么我們在寫RecyclerView的時(shí)候虚汛,如果RecyclerView的父布局、RecyclerView皇帮、item三者的背景只要其中一個(gè)設(shè)置就可以了卷哩,沒有設(shè)置背景就不會渲染,否則就會有過度繪制的情況属拾。
  • 2.父布局套子布局也是盡量只設(shè)置其中一個(gè)背景将谊,除非沒辦法都需要背景。
  • 3.子view一般繪制后是會覆蓋父view渐白,所以一般選擇把背景設(shè)置在子view尊浓。
  • 4.視圖的層級結(jié)構(gòu)能減少就減少,層級越多繪制速度越慢纯衍。
  • 5.盡量少設(shè)置view的透明度栋齿,如果一個(gè)view設(shè)置了alpha,那他需要知道下面的view是什么內(nèi)容襟诸,再繪制自己瓦堵,就是過度繪制。如果是文字有透明度歌亲,可以在色號里就設(shè)置好菇用。

2.3 層級優(yōu)化

  • Android studiol有布局層級的工具,Layout Inspector陷揪,運(yùn)行起來app后惋鸥,可以看到每個(gè)頁面的層級結(jié)構(gòu)。層級太多悍缠,肯定就會造成卡頓卦绣,啟動慢,在啟動優(yōu)化有說扮休,
  • 左邊可以看到布局樹的具體內(nèi)容迎卤。
    布局樹
  • 像ScrollView里面只能放一個(gè)ViewGroup,是不可縮減的玷坠,但 LinearLayout套LinearLayout 是可以通過ConstraintLayout解決的蜗搔,約束布局可以說是結(jié)合了線性布局跟相對布局的優(yōu)點(diǎn)劲藐,能有效減少層級。
    view tree

2.4 使用merge

  • 我們有一些布局是可以通用的樟凄,避免重復(fù)代碼聘芜,就可以使用 include
  • 但是缝龄,如果使用 include汰现,但里面的布局又是一個(gè) ViewGroup 的話,就會造成層級過多叔壤,這個(gè)時(shí)候就可以使用 merge 標(biāo)簽了瞎饲,里面的子view根據(jù)會外部include地方的ViewGroup來排列,從而減少層級炼绘。
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <include layout="@layout/layout_head" />
</LinearLayout>


<!--  layout_head -->
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_head"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:text="text1"
        android:textSize="16sp" />
 

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:text="text2"
        android:textSize="16sp" />
</merge>

  • 可以看到使用merge 里面的 view 直接在 LinearLayout 的層級里嗅战。

2.5 ViewStub

  • 我們有時(shí)根據(jù)需求,先把布局畫好俺亮,然后把 android:visibility 設(shè)置成 "invisible" 或者是 "gone" 驮捍,invisiblegone 雖然看不見,但他們還是有初始化脚曾,占用這內(nèi)存和資源东且,前者還占用著位置。
  • 我們可以使用 ViewStub 來包裹這些需要隱藏顯示的 view本讥,它是一個(gè)輕量級的view相速,不可見不占用資源菩貌,只有當(dāng)設(shè)置 inflate 時(shí)才初始化顯示娘侍。

<ViewStub
    android:id="@+id/viewStub_title"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout="@layout/layout_title"
    app:layout_constraintTop_toTopOf="parent" />


<!-- layout_title -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:orientation="horizontal">

    <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:id="@+id/image_back"
        android:padding="12dp"
        android:src="@drawable/ic_back" />

    <TextView
        android:layout_width="0dp"
        android:id="@+id/tv_title"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center"
        android:text="title" />
</LinearLayout>
  • 在Activity中 viewStub.inflate() 即可顯示惯疙,但不可重復(fù)調(diào)用inflate();否則報(bào)異常:
    java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.ViewStub.inflate()' on a null object reference邦危。
viewStub = findViewById(R.id.viewStub_title);
viewStub.inflate();
//之后可以初始化里面的view
ImageView ivBack = findViewById(R.id.image_back);
ivBack.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        finish();
    }
});
TextView tvTitle = findViewById(R.id.tv_title);

3.其他優(yōu)化

3.1 不要在onDraw里創(chuàng)建對象

  • 我們經(jīng)常需要自定義view盟劫,在 onDraw() 方法里不要創(chuàng)建對象祷愉,因?yàn)樽远xview繪制莱找,會非常頻繁的調(diào)用 onDraw涣仿,雖然方法里的對象創(chuàng)建用完就被回收掉勤庐,但頻繁的創(chuàng)建銷毀對象會導(dǎo)致內(nèi)存抖動和GC ,而 GC 多了就會卡頓。
    避免ondraw里創(chuàng)建對象

3.2 異步加載布局

Google Asynclayoutinflater庫

  • LayoutInflater加載xml布局的過程會在主線程使用IO讀取XML布局文件進(jìn)行XML解析好港,再根據(jù)解析結(jié)果利用反射創(chuàng)建布局中的View/ViewGroup對象愉镰。這個(gè)過程隨著布局的復(fù)雜度上升,耗時(shí)自然也會隨之增大钧汹。Android為我們提供了 Asynclayoutinflater 把耗時(shí)的加載操作在異步線程中完成丈探,最后把加載結(jié)果再回調(diào)給主線程。
  • Asynclayoutinflater 注意的地方:
  • 1拔莱、使用異步 inflate碗降,那么需要這個(gè) layout 的 parent 的 generateLayoutParams 函數(shù)是線程安全的隘竭。
  • 2、所有構(gòu)建的 View 中必須不能創(chuàng)建 Handler 或者是調(diào)用 Looper.myLooper讼渊;(因?yàn)槭窃诋惒骄€程中加載的动看,異步線程默認(rèn)沒有調(diào)用 Looper.prepare )。
  • 3爪幻、AsyncLayoutInflater 不支持設(shè)置 LayoutInflater.Factory 或者 LayoutInflater.Factory2菱皆;。
  • 4挨稿、不支持加載包含 Fragment 的 layout仇轻。
  • 5、如果 AsyncLayoutInflater 失敗奶甘,那么會自動回退到UI線程來加載布局拯田。

參考文章

Android性能優(yōu)化典范

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市甩十,隨后出現(xiàn)的幾起案子船庇,更是在濱河造成了極大的恐慌,老刑警劉巖侣监,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸭轮,死亡現(xiàn)場離奇詭異,居然都是意外死亡橄霉,警方通過查閱死者的電腦和手機(jī)窃爷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姓蜂,“玉大人按厘,你說我怎么就攤上這事∏” “怎么了逮京?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長束莫。 經(jīng)常有香客問我懒棉,道長,這世上最難降的妖魔是什么览绿? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任策严,我火速辦了婚禮,結(jié)果婚禮上饿敲,老公的妹妹穿的比我還像新娘妻导。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布倔韭。 她就那樣靜靜地躺著暑脆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狐肢。 梳的紋絲不亂的頭發(fā)上添吗,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機(jī)與錄音份名,去河邊找鬼碟联。 笑死,一個(gè)胖子當(dāng)著我的面吹牛僵腺,可吹牛的內(nèi)容都是我干的鲤孵。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼辰如,長吁一口氣:“原來是場噩夢啊……” “哼普监!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起琉兜,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤凯正,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后豌蟋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體廊散,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年梧疲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了允睹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,769評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡幌氮,死狀恐怖缭受,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情该互,我是刑警寧澤米者,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站慢洋,受9級特大地震影響塘雳,放射性物質(zhì)發(fā)生泄漏陆盘。R本人自食惡果不足惜普筹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隘马。 院中可真熱鬧太防,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至酿愧,卻和暖如春沥潭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嬉挡。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工钝鸽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人庞钢。 一個(gè)月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓拔恰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親基括。 傳聞我的和親對象是個(gè)殘疾皇子颜懊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容