編輯:關於Android編程
接上一篇《Android開發性能優化總結(一)》
一、安卓UI性能檢測與優化
UI是安卓應用程序與用戶打交道的最直接途徑,UI設計的好不好,直接影響到用戶的體驗,如果沒有達到他們心目中的自然流暢細節,用戶要是能夠感覺出來,少則影響心情,多則卸載應用;所以一個應用的UI顯示性能問題就不得不被開發人員重視。
1.UI卡頓常見原因:
在UI線程中做了耗時操作,導致UI線程卡頓;
布局Layout過於復雜,無法在16ms內完成渲染;
同一時間動畫執行的次數過多,導致CPU或GPU負載過重;
View過度繪制,導致某些像素在同一幀時間內被繪制多次,從而使CPU或GPU負載過重;
View頻繁的觸發measure、layout,導致measure、layout累計耗時過多及整個View頻繁的重新渲染;
內存頻繁觸發GC過多(同一幀中頻繁創建內存),導致暫時阻塞渲染操作;
冗余資源及邏輯等導致加載和執行緩慢;
2.UI性能分析
2.1使用HierarchyViewer分析UI性能
Android SDK提供了一個工具HierarchyViewer,可以用來分析UI布局復雜程度及冗余等
在\sdk\tools\目錄下有個hierarchyviewer.bat,雙擊可啟動(如下圖),注意,啟動前你需要啟動你的模擬器或者接入你的手機進入調試模式
(1)選中其中一個item,點擊Load View Hierarchy,會出現如下圖:
上圖顯示了我的應用程序中MainActivity的View樹,通過這個樹可以分析出View嵌套的冗余層級,左下角可以輸入View的id直接自動跳轉到中間顯示;Save as PNG用來把左側樹保存為一張圖片;Capture Layers用來保存為psd的PhotoShop素材;右上角為View的總體框架圖;右側居中顯示的是選中View的當前屬性和狀態;右下角顯示當前View在Activity中的位置以及模塊分布等;左下角三個進行切換;Load View Hierarchy用來手動刷新變化(在頁面變更時要手動刷新)。
上圖可以很方便的查看到當前View的許多信息;上圖最底那三個彩色圓點代表了當前View的性能指標,從左到右依次代表測量、布局、繪制的渲染時間,紅色和黃色的點代表速度渲染較慢的View(當然了,有些時候較慢不代表有問題,譬如ViewGroup子節點越多、結構越復雜,性能就越差)。
在自定義View的性能調試時,可以使用HierarchyViewer上面的invalidate Layout和requestLayout按鈕,它可以幫助我們調試自定義View執行invalidate()和requestLayout()過程,我們只需要在代碼的相關地方打上斷點就行了,接下來通過它觀察繪制即可。
2.2使用Android Lint進行資源和布局的優化
Android Lint是SDK Tools 16 (ADT 16)之後才引入的工具,通過它對Android工程源代碼進行掃描和檢查,可發現潛在的問題,以便程序員及早修正這個問題。也就是說,它可以用來檢查你的代碼是否合法,當然,它也可以用來檢測UI布局和資源文件的冗余性。
(1)在Android Studio中使用Lint:
將鼠標放在代碼區點擊右鍵->Analyze->Inspect Code–>界面選擇你要檢測的模塊->OK 出現如下圖:
如上,它提示我們,控件的顯示的字符串最好使用@String的形式,當前布局中有個RelativeLayout冗余了,應該去掉。
(2)在Eclipse中,則提供了一個全局的lint檢查,可以在Window->Show View->other->Lint Warnings調出lint檢查視圖(一般都默認出現在控制台區域),Lint Warnings如下圖
它也給了我們相應的提示和問題出現的位置,大家可以自行嘗試。另外,Eclipse在編譯導出APK時,也會執行lint檢查,這時的lint檢查將更加詳細,你能看到更多的信息。
2.3使用Traceview進行分析優化
關於UI卡頓問題我們還可以通過運行Traceview工具進行分析,它是一個分析器,記錄了應用程序中每個函數的執行時間;我們可以打開DDMS然後選擇一個進程,接著點擊上面的“Start Method Profiling”按鈕(紅色小點變為黑色即開始運行),然後操作我們的卡頓UI(小范圍測試,所以操作最好不要超過5s),完事再點一下剛才按的那個按鈕,稍等片刻即可出現下圖,如下:
整個界面上面是你測試的進程中每個線程運行的時間線,下面是每個方法執行的各個指標的值。通過上圖的時間面板可以直觀發現,整個trace時間段main線程做的事情特別多,其他的做的相對較少。當我們選擇上面的一個線程後可以發現下面列出了該方法的Parents和Children,它主要展示了線程中各個方法的調用信息(CPU使用時間、調用次數等),這些信息就是我們分析UI性能卡頓的核心關注點,所以我們先看幾個重要的屬性說明,如下:
有了對上面Traceview圖表的一個認識之後我們就來看看具體導致UI性能後該如何切入分析,一般Traceview可以定位兩類性能問題:
方法調運一次需要耗費很長時間導致卡頓;
方法調運一次耗時不長,但被頻繁調運導致累計時長卡頓。
譬如有時候我們寫完App在使用時不覺得有啥大的影響,但是當我們啟動完App後靜止在那卻十分費電或者導致設備發熱,這種情況我們就可以打開Traceview然後按照Cpu Time/Call或者Real Time/Call進行降序排列,然後打開可疑的方法及其child進行分析查看,然後再回到代碼定位檢查邏輯優化即可。
Systrace它是對整個系統進行分析(同一時間軸包含應用及SurfaceFlinger、WindowManagerService等模塊、服務運行信息),不過這個工具需要你的設備內核支持trace(命令行檢查:/sys/kernel/debug/tracing)且設備是eng或userdebug版本才可以,所以使用前麻煩自己確認一下。
打開DDMS->點擊上面工具欄的Capture system wide trace using Android systrace->設置時間與選項點擊OK就開始了抓取,接著操作APP,完事生成一個trace.html文件,用Chrome打開即可如下圖:
在浏覽器中浏覽分析該文件我們可以通過鍵盤的W-A-S-D鍵來放大縮小平移視圖,由於上面我們在進行trace時選擇了一些選項,所以上圖生成了左上方相關的CPU頻率、負載、狀態等信息,其中的CPU N代表了CPU核數,每個CPU行的柱狀圖表代表了當前時間段當前核上的運行信息;下面我們再來看看SurfaceFlinger的解釋,如下:
可以看見上面左邊欄的SurfaceFlinger其實就是負責繪制Android程序UI的服務,所以SurfaceFlinger能反應出整體繪制情況,可以關注上圖VSYNC-app一行可以發現前5s多基本都能夠達到16ms刷新間隔,5s多開始到7s多大於了15ms,說明此時存在繪制丟幀卡頓;同時可以發現surfaceflinger一行明顯存在類似不規律間隔,這是因為有的地方是不需要重新渲染UI,所以有大范圍不規律,有的是因為阻塞導致不規律,明顯可以發現0到4s間大多是不需要渲染,而5s以後大多是阻塞導致;對應這個時間點我們放大可以看到每個部分所使用的時間和正在執行的任務,具體如下:
可以發現具體的執行明顯存在超時性能卡頓(原點不是綠色的基本都代表存在一定問題,下面和右側都會提示你選擇的幀相關詳細信息或者alert信息),但是遺憾的是通過Systrace只能大體上發現是否存在性能問題,具體問題還需要通過Traceview或者代碼中嵌入Trace工具類等去繼續詳細分析。
2.5 使用traces.txt文件分析ANR
ANR(Application Not Responding)是Android中應用響應超時的表現;ANR是直接卡死UI不動且必須要解掉的Bug,我們必須盡量在開發時避免它的出現。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPs7Sw8fTptPDv6q3otbQs6O8+7XEQU5S1vfSqtPQyOfPwry4wOCjujwvcD4NCjx1bD4NCjxsaT4NCjxwPrC0vPy0pcP+ysK8/sXJt6KzrMqxQU5So6zSu7Dj49DWtc6qNXOjqMno1sPW0L+qxvRBTlK1r7Swo6zErMjP09DKwrz+xcm3orLFu+G0pbeita+/8kFOUqOpo7s8L3A+DQo8L2xpPg0KPGxpPg0KPHA+ueOypdfoyPtBTlKjrNK7sOPj0Na1zqoxMHOjqMno1sPW0L+qxvRBTlK1r7Swo6zErMjPsru1r7/yo6zWu9PQbG9nzOHKvqOpo7s8L3A+DQo8L2xpPg0KPC91bD4NCjxwPrf+zvGzrMqxQU5So6zSu7Dj49DWtc6qMjBzo6jJ6NbD1tC/qsb0QU5Sta+0sKOsxKzIz7K7ta+/8qOs1rvT0GxvZ8zhyr6jqaO7PC9wPg0KPHA+tbFBTlK3osn6yrGz/cHLbG9nY2F0v8nS1L+0vPu1xGxvZ9LUzeLO0sPHu7m/ydLU1NrPtc2z1ri2qMS/wrzPwtXStb10cmFjZXPOxLz+u/Jkcm9wYm94zsS8/r340NC31s72o6w8L3A+DQo8cD7Iu7rzztLDx9PDdHh0seC8rcb3tPK/qr/J0tS3os/WyOfPwr3hubm31s72o7o8L3A+DQo8YmxvY2txdW90ZT4NCgk8cD4vL8/Uyr69+LPMaWShokFOUreiyfrKsbzkteOhokFOUreiyfq9+LPMsPzD+zwvcD4NCgk8cD4tLS0tLSBwaWQgMTkwNzMgYXQgMjAxNS0xMC0wOCAxNzoyNDozOCAtLS0tLTwvcD4NCgk8cD5DbWQgbGluZTogY29tLmV4YW1wbGUueWFuYm8ubXlhcHBsaWNhdGlvbjwvcD4NCgk8cD4vL9K70KlHQ7XIb2JqZWN00MXPoqOszaizo7/J0tS69sLUPC9wPg0KCTxwPi4uLi4uLjwvcD4NCgk8cD4vL0FOUre9t6i20dW7tPLTodDFz6KjodbYteOjoTwvcD4NCgk8cD5EQUxWSUsgVEhSRUFEUyAoMTgpOjwvcD4NCgk8cD4mcXVvdDttYWluJnF1b3Q7IHByaW89NSB0aWQ9MSBTbGVlcGluZzwvcD4NCgk8cD4=" group="main" sCount=1 dsCount=0 obj=0x7497dfb8 self=0x7f9d09a000
| sysTid=19073 nice=0 cgrp=default sched=0/0 handle=0x7fa106c0a8
| state=S schedstat=( 125271779 68162762 280 ) utm=11 stm=1 core=0 HZ=100
| stack=0x7fe90d3000-0x7fe90d5000 stackSize=8MB
| held mutexes=
at java.lang.Thread.sleep!(Native method)
- sleeping on <0x0a2ae345> (a java.lang.Object)
at java.lang.Thread.sleep(Thread.java:1031)
- locked <0x0a2ae345> (a java.lang.Object)
//真正導致ANR的問題點,可以發現是onClick中有sleep導致。我們平時可以類比分析即可,這裡不詳細說明。
at java.lang.Thread.sleep(Thread.java:985)
at com.example.yanbo.myapplication.MainActivity$1.onClick(MainActivity.java:21)
at android.view.View.performClick(View.java:4908)
at android.view.View$PerformClick.run(View.java:20389)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5743)
at java.lang.reflect.Method.invoke!(Native method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:988)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)
......
//省略一些不常關注堆棧打印
......
2.6 UI性能優化總結
布局優化;盡量使用include、merge、ViewStub標簽,盡量不存在冗余嵌套及過於復雜布局,盡量使用GONE替換INVISIBLE,使用weight後盡量將width和heigh設置為0dp減少運算,Item存在非常復雜的嵌套時考慮使用自定義View來取代,減少measure與layout次數等。
列表及Adapter優化;盡量復用getView方法中的相關View,不重復獲取實例導致卡頓,列表盡量在滑動過程中不進行UI元素刷新等。
背景和圖片等內存分配優化;盡量減少不必要的背景設置,圖片盡量壓縮處理顯示,盡量避免頻繁內存抖動等問題出現。
每個Android應用程序都執行在自己的虛擬機中,那了解Java的一定明白,每個虛擬機必定會有堆內存阈值限制(值得一提的是這個阈值一般都由廠商依據硬件配置及設備特性自己設定,沒有統一標准,可以為64M,也可以為128M等;它的配置是在Android的屬性系統的/system/build.prop中配置dalvik.vm.heapsize=128m即可,若存在dalvik.vm.heapstartsize則表示初始申請大小),也即一個應用進程同時存在的對象必須小於阈值規定的內存大小才可以正常運行。
接著我們運行的App在自己的虛擬機中內存管理基本就是遵循Java的內存管理機制了,系統在特定的情況下主動進行垃圾回收。但是要注意的一點就是在Android系統中執行垃圾回收(GC)操作時所有線程(包含UI線程)都必須暫停,等垃圾回收操作完成之後其他線程才能繼續運行。這些GC垃圾回收一般都會有明顯的log打印出回收類型,常見的如下:
GC_MALLOC——內存分配失敗時觸發;
GC_CONCURRENT——當分配的對象大小超過一個限定值(不同系統)時觸發;
眾所周知,在Java中有些對象的生命周期是有限的,當它們完成了特定的邏輯後將會被垃圾回收;但是,如果在對象的生命周期本來該被垃圾回收時這個對象還被別的對象所持有引用,那就會導致內存洩漏;這樣的後果就是隨著我們的應用被長時間使用,他所占用的內存越來越大。
(1)AndroidStudio中有個Memory窗口如下(在Debug模式下查看內存波動),這裡就不詳細說明了:
(2)DDMS-Heap內存監測工具,窗口如下
選擇工具欄中的update heap既可以在Heap視圖中看到相關內存分配和GC信息
(3)使用MAT內存檢測工具
一個基於Eclipse的內存分析工具,是一個快速、功能豐富的JAVA heap分析工具
這個工具是非常專業級的工具,很多大神使用,但它過於龐大和復雜,這裡不再贅述,有興趣的可以自行搜索
(4)使用LeakCanary內存洩漏檢測工具
前言最近公司項目有一個錄音的錄制和播放動畫需求,然後時間是那麼緊,那麼趕緊開撸。先看效果圖嗯,然後大致就是這樣,按住錄音,然後有一個倒計時,最外層一個進度條,還有一個類似
今天自定義了一個簡單的Android菜單控件。實現方式是:PopupWindow和ListView。現在來給大家分享一下源碼: SHContextMenu.java核心代
本文將介紹如何獲取設備中已經安裝的應用信息,包括:應用名稱、包名、圖標等。獲得信息列表後,選擇某一項記錄還可以啟動對應的應用! 1.代碼實現 pack
WireShark是一個非常准確和穩定的tcp抓包工具,但看其40多m的安裝包就可以想象其功能的強大,借助其功能強大的表達式篩選器,可以迅速的篩選出來我們所需要報文和記錄