Android中布局UI的優化總結

Android應用UI性能分析

在使用App時會發現有些界面啟動卡頓、動畫不流暢、列表等滑動時也會卡頓出現這種情況,可以考慮對UI性能分析。

首先要清楚卡頓的原因,有以下幾種情況:

1. 人為在UI線程中做輕微耗時操作,導致UI線程卡頓;

2. 布局Layout過于復雜,無法在16ms內完成渲染;( Android系統每隔16ms會發出VSYNC信號重繪我們的界面(Activity).為什么是16ms, 因為Android設定的刷新率是60FPS(Frame Per Second), 也就是每秒60幀的刷新率, 約合16ms刷新一次.這就意味著, 我們需要在16ms內完成下一次要刷新的界面的相關運算, 以便界面刷新更新. 然而, 如果我們無法在16ms內完成此次運算會怎樣呢?例如, 假設我們更新屏幕的背景圖片, 需要24ms來做這次運算. 當系統在第一個16ms時刷新界面, 然而我們的運算還沒有結束, 無法繪出圖片. 當系統隔16ms再發一次VSYNC信息重繪界面時, 用戶才會看到更新后的圖片. 也就是說用戶是32ms后看到了這次刷新(注意, 并不是24ms). 這就是傳說中的丟幀(dropped frame),丟幀給用戶的感覺就是卡頓, 而且如果運算過于復雜, 丟幀會更多, 導致界面常常處于停滯狀態, 卡到爆.)

3. 同一時間動畫執行的次數過多,導致CPU或GPU負載過重;

4. View過度繪制,導致某些像素在同一幀時間內被繪制多次,從而使CPU或GPU負載過重;

5. View頻繁的觸發measure、layout,導致measure、layout累計耗時過多及整個View頻繁的重新渲染;

6. 內存頻繁觸發GC過多(同一幀中頻繁創建內存),導致暫時阻塞渲染操作;

7. 冗余資源及邏輯等導致加載和執行緩慢;

8. 臭名昭著的ANR;

如何分析?

分析UI卡頓我們一般都借助工具,通過工具一般都可以直觀的分析出問題原因,從而反推尋求優化方案,具體如下細說各種強大的工具

1. 使用HierarchyViewer分析UI性能

我們可以通過SDK提供的工具HierarchyViewer來進行UI布局復雜程度及冗余等分析
通過命令啟動HierarchyViewer

圖片.png

接下來Hierarchy window窗口打開:

圖片.png

一個Activity的View樹,通過這個樹可以分析出View嵌套的冗余層級,以及每個View在繪制的使用時長也有表示。

2. 使用Lint進行資源及冗余UI布局等優化

冗余資源及邏輯等也可能會導致加載和執行緩慢,這可以使用Link工具,來發現優化這些問題的

在AndroidStudio 1.4版本中使用Lint最簡單的辦法:就是將鼠標放在代碼區點擊右鍵->Analyze->Inspect Code–>界面選擇你要檢測的模塊->點擊確認開始檢測,等待一下后會發現如下結果:

圖片.png

如果存在冗余的UI層級嵌套,會進行高亮顯示, 我們根據提示可以點擊跳進去進行優化處理掉的。

3. 使用Memory監測及GC打印與Allocation Tracker進行UI卡頓分析

由于Android系統會依據內存中不同的內存數據類型分別執行不同的GC操作,常見應用開發中導致GC頻繁執行的原因主要可能是因為短時間內有大量頻繁的對象創建與釋放操作,也就是俗稱的內存抖動現象,或者短時間內已經存在大量內存暫用介于閾值邊緣,接著每當有新對象創建時都會導致超越閾值觸發GC操作

如何查看?

Android Studio 工具提供內存查看器:

圖片.png

根據內存抖動現象,查看log日志進行分析:

圖片.png

如何看到,這種不停的大面積打印GC導致所有線程暫停的操作必定會導致UI視覺的卡頓,所以我們要避免此類問題的出現,具體的常見優化方式如下:

  1. 檢查代碼,盡量避免有些頻繁觸發的邏輯方法中存在大量對象分配;
  2. 盡量避免在多次for循環中頻繁分配對象;
  3. 避免在自定義View的onDraw()方法中執行復雜的操作及創建對象(譬如Paint的實例化操作不要寫在onDraw()方法中等);
  4. 對于并發下載等類似邏輯的實現盡量避免多次創建線程對象,而是交給線程池處理。

有了上面說明GC導致的性能后我們就該定位分析問題了,我們可以通過運行DDMS->Allocation Tracker標簽打開一個新窗口,然后點擊Start Tracing按鈕,接著運行你想分析的代碼,運行完畢后點擊GetAllocations按鈕就能夠看見一個已分配對象的列表,如下:

圖片.png

UI布局優化措施有哪些?

  • 盡可能的減少布局的嵌套層級(Google建議View樹的高度不宜超過10層。)
    以前我們用Eclipse寫代碼時,自動生成的模板是以LinearLayout為根節點的,但是后面變成了RelativeLayout為根節點。
    RelativeLayout可以讓視圖樹的層級少,但是LinearLayout的測量效率要高。
    如果使用RelativeLayout,需要盡量避免嵌套;如果使用LinearLayout,保證層級不能太深。

  • 使用HierarchyViewer工具分析視圖樹,剔除沒有用到的布局元素

  • 不用設置不必要的背景,避免過度繪制,比如父控件設置了背景色,子控件完全將父控件給覆蓋的情況下,那么父控件就沒必要設置背景了

  • <include>標簽
    編寫頁面布局是可以將通用的UI提取出來,使用的時候使用<include>標簽將其引入。但是<include>標簽并不能減少布局層級,只能增加代碼可讀性。

  • <merge>標簽
    問:使用merge標簽有哪些優點?
    答:<merge/>標簽可以用于減少View樹的層次來優化Android的布局,使用<include>包含布局的時候,系統會自動忽略merge層級。

    問:merge標簽有什么缺點么?
    答:1. <merge/>只能作為XML布局的根標簽使用。
    2.當Inflate以<merge/>開頭的布局文件時,必須指定一個父ViewGroup,并且必須設定attachToRoot為true。

    問:什么情況考慮使用Merge標簽?
    答:1.子視圖不需要指定任何針對父視圖的布局屬性,例子中TextView僅僅需要直接添加到父視圖上用于顯示就行。
    2.假如需要在LinearLayout里面嵌入一個布局(或者視圖),而恰恰這個布局(或者視圖)的根節點也是LinearLayout,這樣就多了一層沒有用的嵌套,無疑這樣只會拖慢程序速度。而這個時候如果我們使用merge根標簽就可以避免那樣的問題。

  • <ViewStub>標簽
    問:使用ViewStub標簽有哪些優點?
    答:<ViewStub>標簽可以實現對一個view進行延遲加載,是一個輕量級的布局,它聲明在xml布局中,在Activity加載布局時,被它包裹的View看不見也不占布局位置,最重要的是:它不會實例化里邊的View,也不會產生繪制的消耗,直到它被主動inflate.

    問:使用ViewStub有啥需要注意的嗎?
    答:1. 在要渲染的布局中并不支持<merge/>標簽。
    2.ViewStub.infalte方法不能調用兩次,否則會出現異常。(因為一旦ViewStub visible/inflated,則ViewStub將從視圖框架中移除,其id也會失效)

    問:ViewStub標簽和View.GONE的區別?
    答:ViewStub標簽只有在顯示時才去渲染整個布局,但是View.GONE在初始化布局時就已經添加在布局樹上了。

<ViewStub>用法:

((ViewStub)findViewById(R.id.viewstub)).setVisibility(View.VISIBLE);
 // or
View importPanel = ((ViewStub) findViewById(R.id.viewstub)).inflate();

Android性能優化之App應用啟動分析與優化

App啟動方式
通常來說, 一個App啟動也會分如下兩種不同的狀態:

  • 1)冷啟動
    當啟動應用時,后臺沒有該應用的進程,這時系統會重新創建一個新的進程分配給該應用,這個啟動方式就是冷啟動。冷啟動因為系統會重新創建一個新的進程分配給它,所以會先創建和初始化Application類,再創建和初始化MainActivity類(包括一系列的測量、布局、繪制),最后顯示在界面上。

  • 2.)熱啟動
    當啟動應用時,后臺已有該應用的進程(例:按back鍵、home鍵,應用雖然會退出,但是該應用的進程是依然會保留在后臺,可進入任務列表查看),所以在已有進程的情況下,這種啟動會從已有的進程中來啟動應用,這個方式叫熱啟動。熱啟動因為會從已有的進程中來啟動,所以熱啟動就不會走Application這步了,而是直接走MainActivity(包括一系列的測量、布局、繪制),所以熱啟動的過程只需要創建和初始化一個MainActivity就行了,而不必創建和初始化Application,因為一個應用從新進程的創建到進程的銷毀,Application只會初始化一次。

啟動時間的測量
關于Activity啟動時間的定義
對于Activity來說,啟動時,首先執行的是onCreate()、onStart()、onResume()這些生命周期函數,但即使這些生命周期方法回調結束了,應用也不算已經完全啟動,還需要等View樹全部構建完畢,一般認為,setContentView中的View全部顯示結束了,算作是應用完全啟動了。

那么這個時間,實際上是Activity啟動,到Layout全部顯示的過程,但是要注意,這里并不包括數據的加載,因為很多App在加載時會使用懶加載模式,即數據拉取后,再刷新默認的UI。

基于上面的啟動流程我們盡量做到如下幾點

    1. Application的創建過程中盡量少的進行耗時操作
    1. 首屏Activity的渲染

關于Application

Application是程序的主入口,特別是很多第三方SDK都會需要在Application的onCreate里面做很多初始化操作,一般來說我們可以將這些初始化放在一個單獨的線程中處理, 為了方便今后管理,
優化的方法,無非是通過以下幾個方面:

  • 1) 異步初始化
    這個很簡單,就是讓App在onCreate里面盡可能的少做事情,而利用手機的多核特性,盡可能的利用多線程,例如一些第三方框架的初始化,如果能放線程,就盡量的放入線程中,最簡單的,你可以直接new Thread(),當然,你也可以通過公共的線程池來進行異步的初始化工作,這個是最能夠壓縮啟動時間的方式

  • 2) 后臺任務
    使用IntentService不同于Service, 它是工作在后臺線程的.

  • 3) 界面預加載
    當系統加載一個Activity的時候,onCreate()是一個耗時過程,那么在這個過程中,系統為了讓用戶能有一個比較好的體驗,實際上會先繪制一些初始界面,類似于PlaceHolder。
    系統首先會讀取當前Activity的Theme,然后根據Theme中的配置來繪制,當Activity加載完畢后,才會替換為真正的界面。所以,Google官方提供的解決方案,就是通過android:windowBackground屬性,來進行加載前的配置,同時,這里不僅可以配置顏色,還能配置圖片.

自定義View如何優化內存?

  • 不建議在draw或者layout的過程中去實例化對象
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,615評論 25 708
  • 太長不看版:在 Android UI 布局過程中,遵守一些慣用、有效的布局原則,可以制作出高效且復用性高的 UI。...
    Mupceet閱讀 3,875評論 0 14
  • 注意事項: 布局優化;盡量使用include、merge、ViewStub標簽,盡量不存在冗余嵌套及過于復雜布局(...
    HarryXR閱讀 5,235評論 1 19
  • 原文鏈接:https://yuchao.wang/article?id=37 應用瘦身 首先打包 xxx.apk ...
    wangyuchao閱讀 1,003評論 0 0
  • 從2013年-2017年,四年時光,轉眼即逝。 或許因為自己還要在學校待三年,所以,未曾想過自己會傷感,可能是被她...
    Xiangyan閱讀 305評論 0 0