Android客戶端性能優(yōu)化實踐

一、關(guān)于App性能優(yōu)化

1. 性能優(yōu)化分類

Google官方給出的性能優(yōu)化教程,主要分為以下幾類:
1)布局與UI渲染優(yōu)化
2)運算與內(nèi)存管理優(yōu)化
3)電池電量優(yōu)化
4)高效代碼相關(guān)Tips
5)多線程操作

官方鏈接:https://developer.android.com/training/best-performance.html
中文博客:https://aeli.gitbooks.io/android-training-course/content/best-performance.html

2. 渲染性能與UI卡頓

大多數(shù)用戶感知到的卡頓等性能問題的最主要根源都是因為渲染性能,Android系統(tǒng)很有可能無法及時完成那些復(fù)雜的界面渲染操作,Android系統(tǒng)每隔16ms發(fā)出VSYNC信號,觸發(fā)對UI進行渲染,如果每次渲染都成功,這樣就能夠達(dá)到流暢的畫面所需要的60fps,為了能夠?qū)崿F(xiàn)60fps,這意味著程序的大多數(shù)操作都必須在16ms內(nèi)完成。

圖1-1 Android界面渲染

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

圖1-2 Android渲染丟幀

用戶容易在UI執(zhí)行動畫或者滑動ListView的時候感知到卡頓不流暢,是因為這里的操作相對復(fù)雜,容易發(fā)生丟幀的現(xiàn)象,從而感覺卡頓。有很多原因可以導(dǎo)致丟幀,也許是因為你的layout太過復(fù)雜,無法在16ms內(nèi)完成渲染,有可能是因為你的UI上有層疊太多的繪制單元,還有可能是因為動畫執(zhí)行的次數(shù)過多。這些都會導(dǎo)致CPU或者GPU負(fù)載過重。

二、工具篇

我們可以通過一些工具來定位問題,比如可以使用HierarchyViewer來查找Activity中的布局是否過于復(fù)雜,也可以使用手機設(shè)置里面的開發(fā)者選項,打開Show GPU Overdraw等選項進行觀察。你還可以使用TraceView來觀察CPU的執(zhí)行情況,更加快捷的找到性能瓶頸。

1. HierarchyViewer

HierarchyViewer是我們優(yōu)化程序的工具之一,它是Android自帶的非常有用的工具,可以幫助我們更好地檢測和設(shè)計用戶界面,能夠以可視化的角度直觀地獲得UI布局設(shè)計結(jié)構(gòu)和各種屬性的信息,幫助我們優(yōu)化布局設(shè)計。但是獨立的HierarchyViewer已廢棄,Android Studio中建議使用Android Device Monitor。
需使用模擬器或開發(fā)版的真機,因為需要系統(tǒng)中的Hierarchy Viewer服務(wù)開啟。
使用方式:
1)點擊Tools->Android->Android Device Monitor,進入Android Device Monitor,切換到HierarchyViewer標(biāo)簽。

圖2-1 Android HierarchyViewer標(biāo)簽

2)或者直接進入獨立的HierarchyViewer:進入sdk/tools路徑,找到HierarchyViewer,雙擊運行。

圖2-2 運行HierarchyViewer

3)在左側(cè)樹狀結(jié)構(gòu)中選中要查看的activity,加載完畢后會顯示當(dāng)前界面的樹狀結(jié)構(gòu)層次,從PhoneWindow.DecorView出發(fā),右側(cè)是xml內(nèi)定義的布局結(jié)構(gòu),大小可以縮放,可以拖動,右側(cè)是屬性預(yù)覽區(qū)域。

圖2-3 界面樹狀層次

4)觀察單個view,選擇單個view后會出現(xiàn)如下所示圖形,可以看到Measure、Layout、Draw的耗時,從左到右三個圓點,分別表示對Measure、Layout、Draw耗時性能的評級,按照性能從高到低,分別為綠色、黃色和紅色,浮窗上是具體的數(shù)值

圖2-4 界面控制渲染耗時

通過使用HierarchyViewer,我們可以通過減少頁面層級,優(yōu)化布局結(jié)構(gòu)來實現(xiàn)UI性能的優(yōu)化,針對每個單獨控件的渲染性能數(shù)據(jù),可以獨立的針對某控件做特殊優(yōu)化。

2. GPU過度繪制

過度繪制就是Overdraw,是指在一幀的時間內(nèi)(16.67ms)像素被繪制了多次,理論上一個像素每次只繪制一次是最優(yōu)的,但是由于重疊的布局導(dǎo)致一些像素會被多次繪制,而每次繪制都會對應(yīng)到CPU的一組繪圖命令和GPU的一些操作,產(chǎn)生額外開銷,當(dāng)這個操作耗時超過16.67ms時,就會出現(xiàn)掉幀現(xiàn)象,也就是我們所說的卡頓,所以應(yīng)盡量減少Overdraw的發(fā)生。
Android提供了測量Overdraw的選項,在開發(fā)者選項-調(diào)試GPU過度繪制(Show GPU Overdraw),打開選項就可以看到當(dāng)前頁面Overdraw的狀態(tài),就可以觀察屏幕的繪制狀態(tài)。該工具會使用三種不同的顏色繪制屏幕,來指示overdraw發(fā)生在哪里以及程度如何,其中:

無顏色:沒有overdraw。像素只畫了一次。
藍(lán)色:overdraw 1倍。像素繪制了兩次。大片的藍(lán)色還是可以接受的(若整個窗口是藍(lán)色的,可以擺脫一層)。
綠色:overdraw 2倍。像素繪制了三次。中等大小的綠色區(qū)域是可以接受的但你應(yīng)該嘗試優(yōu)化、減少它們。
淺紅:overdraw 3倍。像素繪制了四次,小范圍可以接受。
暗紅:overdraw 4倍。像素繪制了五次或者更多。這是錯誤的,要修復(fù)它們。

圖3-1 Android過度繪制

減少過度繪制Tips:

  • 不同控件容器特點不同,根據(jù)不同場景合理選擇控件容器,例如RelativeLayout
    相對于LinearLayout,在某些場景下可減少嵌套層次,但LinearLayout使用簡單,效率也更高
  • 去掉window的默認(rèn)背景,DecorView會創(chuàng)建默認(rèn)背景,而自定義布局會蓋上一層背景圖或背景色,導(dǎo)致默認(rèn)背景無用,帶來繪制性能損耗
  • 去掉其他不必要的背景,減少背景重疊或覆蓋
  • 多使用輕量級的ViewStub(只有設(shè)置可見或inflate時才會實例化內(nèi)部的布局),避免將所有View寫上,并動態(tài)控制GONE、VISIBLE
  • 使用Merge,減少一層View層級
  • 慎用Alpha,對一個View做Alpha轉(zhuǎn)化,需要先將View繪制出來,再做Alpha轉(zhuǎn)化,最后將轉(zhuǎn)換后效果繪制在界面上,也就是需要對當(dāng)前View繪制兩遍

3. Allocation Tracker

Android系統(tǒng)會依據(jù)內(nèi)存中不同的內(nèi)存數(shù)據(jù)類型分別執(zhí)行不同的GC操作,常見的導(dǎo)致GC頻繁執(zhí)行的原因主要可能是因為短時間內(nèi)有大量頻繁的對象創(chuàng)建與釋放操作,也就是俗稱的內(nèi)存抖動現(xiàn)象,或者短時間內(nèi)已經(jīng)存在大量內(nèi)存占用介于閾值邊緣,每當(dāng)有新對象創(chuàng)建時都會導(dǎo)致超越閾值觸發(fā)GC操作。
Allocation Tracker,內(nèi)存分配跟蹤器,可以記錄代碼運行過程中的內(nèi)存分配情況,包括已分配對象的調(diào)用棧、大小、代碼位置等,可以幫助我們發(fā)現(xiàn)在相同的調(diào)用棧中,短時間迅速分配與回收的大量相似對象,也可以幫助我們發(fā)現(xiàn)代碼中可能對內(nèi)存性能產(chǎn)生不良影響的地方。

1)打開Android Studio中的Android Monitor控制器,進入Memory內(nèi)存監(jiān)控窗口,點擊如下所示的Start Allocation Tracking按鈕,開始跟蹤

圖3-2 啟動Allocation Tracking

2)操作手機相關(guān)待檢測頁面,執(zhí)行相應(yīng)路徑代碼,此時Allocation Tracker會在后臺不斷記錄,注意操作時間不要過長,否則會產(chǎn)生過度的記錄,不便于發(fā)現(xiàn)問題。然后再點擊一次Start Allocation Tracking按鈕,停止跟蹤

圖3-3 Allocation Tracking結(jié)果

3)最終會形成上圖所示的陰影區(qū)域,表示內(nèi)存跟蹤的時間段,然后會生存以.alloc命名的文件,默認(rèn)會以線程來分組

圖3-4 Allocation Tracking日志

4)可選擇Group by Method或Group by Allocator,每組內(nèi)的數(shù)據(jù)默認(rèn)按照對象的分配順序排列,可點擊每條展開,并可最終跟蹤到最底層調(diào)用,Count表示分配的內(nèi)存的次數(shù),Size表示分配內(nèi)存的大小

圖3-5 結(jié)果排序與調(diào)用堆棧

5)若選擇Group by Allocator,則按照包名來分組,組內(nèi)排序和使用與上述相同

圖3-6 按包名分組

6)標(biāo)題欄有個統(tǒng)計按鈕,點擊后,可以生存炫酷的內(nèi)存分配輪胎圖或柱狀圖,默認(rèn)是輪胎圖,輪胎圖是以圓心為起點,最外層是其內(nèi)存實際分配的對象,每一個同心圓可能被分割成多個部分,代表了其不同的子孫,每一個同心圓代表他的一個后代,每個分割的部分代表了某一帶人有多人,你雙擊某個同心圓中某個分割的部分,會變成以你點擊的那一代為圓心再向外展開。如果想回到原始狀態(tài),雙擊圓心就可以了

圖3-7 內(nèi)存分配輪胎圖

4. TraceView

TraceView 是 Android 平臺特有的數(shù)據(jù)采集和分析工具,它主要用于分析 Android 中應(yīng)用程序的 hotspot。TraceView 本身只是一個數(shù)據(jù)分析工具,而數(shù)據(jù)的采集則需要使用 Android SDK 中的 Debug 類或者利用Android Device Monitor工具:

  • 在一些關(guān)鍵代碼段開始前調(diào)用 Android SDK 中 Debug 類的 startMethodTracing 函數(shù),并在關(guān)鍵代碼段結(jié)束前調(diào)用 stopMethodTracing 函數(shù)。這兩個函數(shù)運行過程中將采集運行時間內(nèi)該應(yīng)用所有線程(注意,只能是 Java 線程)的函數(shù)執(zhí)行情況,并將采集數(shù)據(jù)保存到 /mnt/sdcard/ 下的一個文件中,然后利用 SDK 中的TraceView工具來分析這些數(shù)據(jù)
  • 借助Android Device Monitor工具,采集系統(tǒng)中某個正在運行的進程的函數(shù)調(diào)用信息。對開發(fā)者而言,此方法適用于沒有目標(biāo)應(yīng)用源代碼的情況

1)打開Android Studio,進入Tools->Android->Android Device Monitor,選擇相關(guān)進程,并點擊Start Method Tracing按鈕,開始追蹤

圖3-8 Start Method Tracing

2)操作應(yīng)用待檢測部分,不要時間太久,然后再次點擊Start Method Tracing按鈕,停止追蹤,便可自動生成.trace文件

圖3-9 Trace文件

TraceViewUI 劃分為上下兩個面板,Timeline Panel(時間線面板)和 Profile Panel(分析面板),Timeline Panel 又可細(xì)分為左右兩個 Panel:

左邊 Panel 顯示的是測試數(shù)據(jù)中所采集的線程信息
右邊 Pane 所示為時間線,時間線上是每個線程測試時間段內(nèi)所涉及的函數(shù)調(diào)用信息。這些信息包括函數(shù)名、函數(shù)執(zhí)行時間等

下半部分的Profile Panel 是 TraceView 的核心界面,主要展示了某個線程(先在 Timeline Panel 中選擇線程)中各個函數(shù)調(diào)用的情況,包括 CPU 使用時間、調(diào)用次數(shù)等信息。而這些信息正是查找 hotspot 的關(guān)鍵依據(jù)。下表是Profile Panel 中比較重要的列名及其描述:

圖3-10 Profile Panel列名與描述

可點擊上述任一排序項進行排序

3)雙擊任一條記錄,可展開詳細(xì)信息,包括調(diào)用者(Parents)和子函數(shù)(Children),可詳細(xì)的對調(diào)用次數(shù)過多的函數(shù)和每次執(zhí)行時間過長的函數(shù)做排查,便于發(fā)現(xiàn)HotSpot

二、布局優(yōu)化

1. 抽象布局標(biāo)簽

1)<include>標(biāo)簽
include標(biāo)簽常用于將布局中的公共部分提取出來供其他layout共用,以實現(xiàn)布局模塊化,這在布局編寫方便提供了大大的便利。

2)<viewstub>標(biāo)簽
viewstub標(biāo)簽同include標(biāo)簽一樣可以用來引入一個外部布局,但是,viewstub引入的布局默認(rèn)不會擴張,即既不會占用顯示也不會占用位置,從而在解析layout時節(jié)省cpu和內(nèi)存。viewstub常用來引入那些默認(rèn)不會顯示,只在特殊情況下顯示的布局,如進度布局、網(wǎng)絡(luò)失敗顯示刷新布局、信息出錯出現(xiàn)提示布局等。

3)<merge>標(biāo)簽
在使用了include后可能導(dǎo)致布局嵌套過多,多余不必要的layout節(jié)點,從而導(dǎo)致解析變慢,merge標(biāo)簽可用于兩種典型情況:

  • 布局頂結(jié)點是FrameLayout且不需要設(shè)置background或padding等屬性,可以用merge代替,因為Activity內(nèi)容試圖的parent view就是個FrameLayout,所以可以用merge消除只剩一個
  • 某布局作為子布局被其他布局include時,使用merge當(dāng)作該布局的頂節(jié)點,這樣在被引入時頂結(jié)點會自動被忽略,而將其子節(jié)點全部合并到主布局中

2. 去除不必要的嵌套和View節(jié)點

1)首次不需要使用的節(jié)點設(shè)置為GONE或使用viewstub
2)使用RelativeLayout代替LinearLayout

3. 減少不必要的infalte

1)對于inflate的布局可以直接緩存,用全部變量代替局部變量,避免下次需再次inflate

三、代碼優(yōu)化

1. 降低執(zhí)行時間

包括:緩存、數(shù)據(jù)存儲優(yōu)化、算法優(yōu)化、JNI、邏輯優(yōu)化等幾種優(yōu)化方式 。

1)緩存
緩存主要包括對象緩存、IO緩存、網(wǎng)絡(luò)緩存、DB緩存,對象緩存能減少內(nèi)存的分配,IO緩存減少磁盤的讀寫次數(shù),網(wǎng)絡(luò)緩存減少網(wǎng)絡(luò)傳輸,DB緩存較少Database的訪問次數(shù)。
在內(nèi)存、文件、數(shù)據(jù)庫、網(wǎng)絡(luò)的讀寫速度中,內(nèi)存都是最優(yōu)的,且速度數(shù)量級差別,所以盡量將需要頻繁訪問或訪問一次消耗較大的數(shù)據(jù)存儲在緩存中。
Android中常使用緩存:

  • 線程池,對線程的緩存
  • Android圖片緩存
  • 消息緩存,通過handler.obtainMessage復(fù)用之前的message,如下:handler.sendMessage(handler.obtainMessage(0, object));
  • ListView緩存
  • 網(wǎng)絡(luò)緩存,數(shù)據(jù)庫緩存http response,根據(jù)http頭信息中的Cache-Control域確定緩存過期時間
  • 文件IO緩存,使用具有緩存策略的輸入流,BufferedInputStream替代InputStream,BufferedReader替代Reader,BufferedReader替代BufferedInputStream.對文件、網(wǎng)絡(luò)IO皆適用
  • layout緩存
  • 其他需要頻繁訪問或訪問一次消耗較大的數(shù)據(jù)緩存

2)數(shù)據(jù)存儲優(yōu)化

包括數(shù)據(jù)類型、數(shù)據(jù)結(jié)構(gòu)的選擇:

  • 數(shù)據(jù)類型選擇
    字符串拼接用StringBuilder代替String,在非并發(fā)情況下用StringBuilder代替StringBuffer。
    64位類型如long double的處理比32位如int慢。
    使用SoftReference、WeakReference相對正常的強應(yīng)用來說更有利于系統(tǒng)垃圾回收。
    final類型存儲在常量區(qū)中讀取效率更高。
    枚舉值相比常量會占用更大的內(nèi)存空間和運行開銷,適用于強類型安全的場景,對于普通常量的匯總,就不一定需要。
    盡量使用原始數(shù)據(jù)類型,避免頻繁的自動裝箱。
    避免使用非靜態(tài)內(nèi)部類,當(dāng)創(chuàng)建并實例化了一個非靜態(tài)內(nèi)部類,內(nèi)部就包含一個指向外部類型的隱含引用,如果這個內(nèi)部類實例比外部類型存活的時間還長,那即使不需要這個外部類型,它還是會被保存在內(nèi)存中。
    LocalBroadcastManager代替普通BroadcastReceiver,效率和安全性都更高。
    如果對象中某個方法的調(diào)用不依賴于該對象的實例化,則建議將該方法定義成static,可提高訪問與調(diào)用效率。
  • 數(shù)據(jù)結(jié)構(gòu)選擇
    常見的數(shù)據(jù)結(jié)構(gòu)選擇如:
    ArrayList和LinkedList的選擇,ArrayList隨機訪問更快,LinkedList更占內(nèi)存、隨機插入刪除更快速、擴容效率更高。
    HashMap、LinkedHashMap、HashSet的選擇,HashMap為鍵值對數(shù)據(jù)結(jié)構(gòu),LinkedHashMap可以記住加入次序的hashMap,HashSet不允許重復(fù)元素。
    HashMap、WeakHashMap選擇,WeakHashMap中元素可在適當(dāng)時候被系統(tǒng)垃圾回收器自動回收,所以適合在內(nèi)存緊張型中使用。
    Collections.synchronizedMap和ConcurrentHashMap的選擇,ConcurrentHashMap為細(xì)分鎖,鎖粒度更小,并發(fā)性能更優(yōu)。Collections.synchronizedMap為對象鎖,自己添加函數(shù)進行鎖控制更方便。
    Android也提供了一些性能更優(yōu)的數(shù)據(jù)類型,如SparseArray、SparseBooleanArray、SparseIntArray、Pair。
    Sparse系列的數(shù)據(jù)結(jié)構(gòu)是為key為int情況的特殊處理,采用二分查找及簡單的數(shù)組存儲,加上不需要泛型轉(zhuǎn)換的開銷,相對Map來說性能更優(yōu)。
    當(dāng)需要存儲一對元數(shù)據(jù)數(shù)組,例如(Foo,Bar),采用兩個平行的二維數(shù)組Foo[ ]和Bar[ ],要比直接存儲對象(Foo,Bar)數(shù)組的效率高。
    使用增強的for-each循環(huán)對實現(xiàn)了iterable接口的collections以及數(shù)組做遍歷,for (Foo a : mArray)和for (int i = 0; i < len; ++i)效率最高,避免使用for (int i = 0; i < mArray.length; ++i)

3)算法優(yōu)化
這個主題比較大,需要具體問題具體分析,盡量不用O(n*n)時間復(fù)雜度以上的算法,必要時候可用空間換時間。查詢考慮hash和二分,盡量不用遞歸。

4)JNI
Android應(yīng)用程序大都通過Java開發(fā),需要編譯器將Java字節(jié)碼轉(zhuǎn)換成本地代碼運行,而本地代碼可以直接由設(shè)備管理器直接執(zhí)行,節(jié)省了中間步驟,所以執(zhí)行速度更快。不過需要注意從Java空間切換到本地空間需要開銷,同時編譯器也能生成優(yōu)化的本地代碼,所以糟糕的本地代碼不一定性能更優(yōu)。

5)邏輯優(yōu)化
主要是理清程序業(yè)務(wù)邏輯,減少不必要的操作和分支判斷等。

2. 異步,利用多線程提高TPS

充分利用多核CPU優(yōu)勢,利用線程解決密集型計算、IO、網(wǎng)絡(luò)等操作。

3. 提前或延遲操作,錯開時間段提高TPS

1)延遲操作
不在Activity、Service、BroadcastReceiver的生命周期等對響應(yīng)時間敏感函數(shù)中執(zhí)行耗時操作,可適當(dāng)delay,Java中延遲操作可使用ScheduledExecutorService, Android中除了支持ScheduledExecutorService之外,還有一些delay操作,如handler.postDelayed,handler.postAtTime,handler.sendMessageDelayed,View.postDelayed,AlarmManager定時等。

2)提前操作
對于第一次調(diào)用較耗時操作,可統(tǒng)一放到初始化中,將耗時提前。如得到壁紙wallpaperManager.getDrawable();

四、關(guān)于內(nèi)存泄漏

1. 使用Android Studio Java Heap Dump檢測

1)打開Android Studio,進入Android Device Monitor,切換到Memory監(jiān)控窗口
2)選擇待調(diào)試進程com.baidu.ls.waimai,并操作App中可能發(fā)生內(nèi)存泄漏的地方,點擊Initiate GC按鈕進行強制GC,然后點擊Dump Java Heap按鈕,導(dǎo)出.hprof格式文件

圖4-1 Dump Java Heap

3)打開.hprof文件,可選擇Package Tree View,選擇應(yīng)用包內(nèi)想要查看的對象類型,可在右側(cè)Instance窗口查看該類型已存在的實例化對象數(shù)量,在下面Reference Tree窗口查看該對象引用路徑

圖4-2 hprof文件分析

4)上面窗口有個Total Count篩選項,可以看到該對象目前在內(nèi)存中存在的數(shù)量,若某個對象數(shù)量存在異常,比如此時App所有的點菜頁已關(guān)閉,但ShopMenuFragment數(shù)量不為0,則該對象很可能發(fā)生內(nèi)存泄漏,需要根據(jù)reference tree做相關(guān)排查,并找到最終的GC Root

2. 使用LeakCanary檢測

LeakCanary是一個用于檢測內(nèi)存泄露的開源類庫,提供了一種便捷、自動的檢測方式,并產(chǎn)出可視化報告,以很直白的方式將內(nèi)存泄露鏈條展示給我們。

1)接入,在build.gradle中根據(jù)不同的編譯方式,選擇加入不同的引用方式,

dependencies { 
        debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3' 
        releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3' 
     }

2)LeakCanary.install(context) 會返回一個預(yù)定義的 RefWatcher,同時也會啟用一個 ActivityRefWatcher,用于自動監(jiān)控調(diào)用 Activity.onDestroy() 之后泄露的 activity。
在Application中進行配置:

public class ExampleApplication extends Application { 
  public static RefWatcher getRefWatcher(Context context) { 
    ExampleApplication application = (ExampleApplication) context.getApplicationContext(); 
    return application.refWatcher; 
  } 

  private RefWatcher refWatcher; 
  @Override public void onCreate() { 
    super.onCreate(); 
    refWatcher = LeakCanary.install(this); 
  } 
} 

3)使用RefWatcher監(jiān)控Fragment

public abstract class BaseFragment extends Fragment { 
  @Override public void onDestroy() { 
    super.onDestroy(); 
    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity()); 
    refWatcher.watch(this); 
  } 
} 

4)產(chǎn)出可視化報告

圖4-3 LeakCanary可視化報告

3. 常見避免內(nèi)存泄露方式

1)對Activity等組件的引用應(yīng)該控制在Activity的生命周期之內(nèi),如果不能就考慮使用getApplicationContext或者getApplication,以避免Activity被外部長生命周期的對象引用而泄露
2)盡量不要在靜態(tài)變量或者靜態(tài)內(nèi)部類中使用非靜態(tài)外部成員變量(包括
context ),即使要使用,也要考慮適時把外部成員變量置空;也可以在內(nèi)部類中使用弱引用來引用外部類的變量
3)對于生命周期比Activity長的內(nèi)部類對象,并且內(nèi)部類中使用了外部類的成員變量,可以這樣做避免內(nèi)存泄漏:將內(nèi)部類改為靜態(tài)內(nèi)部類、靜態(tài)內(nèi)部類中使用弱引用來引用外部類的成員變量
4)Handler持有的引用對象最好使用弱引用,資源釋放時也可以清空 Handler 里面的消息。比如在 Activity onStop 或者 onDestroy 的時候,取消掉該 Handler 對象的 Message和 Runnable
5)在 Java 的實現(xiàn)過程中,也要考慮其對象釋放,最好的方法是在不使用某對象時,顯式地將此對象賦值為 null,比如使用完Bitmap 后先調(diào)用 recycle(),再賦為null,清空對圖片等資源有直接引用或者間接引用的數(shù)組(使用 array.clear() ; array = null)等,最好遵循誰創(chuàng)建誰釋放的原則
6)正確關(guān)閉資源,對于使用了BraodcastReceiver,ContentObserver,F(xiàn)ile,游標(biāo) Cursor,Stream,Bitmap等資源的使用,應(yīng)該在Activity銷毀時及時關(guān)閉或者注銷
7)保持對對象生命周期的敏感,特別注意單例、靜態(tài)對象、全局性集合等的生命周期

六、參考鏈接

  1. Android官方性能優(yōu)化:
    https://developer.android.com/training/best-performance.html
  2. Android官方性能典范教程:
    http://hukai.me/android-performance-patterns/
  3. 性能優(yōu)化博客合輯:
    https://github.com/Juude/awesome-android-performance
  4. Trinea性能優(yōu)化合輯:
    http://www.trinea.cn/android/performance/
  5. Android性能優(yōu)化多途徑實現(xiàn):
    http://blog.csdn.net/yanbober/article/details/48394201
  6. Android Studio官方profile工具使用:
    https://developer.android.com/studio/profile/index.html
  7. Android內(nèi)存泄露總結(jié):
    https://yq.aliyun.com/articles/3009
  8. Android TraceView使用:
    http://www.cnblogs.com/sunzn/p/3192231.html
  9. LeakCanary使用:
    http://www.itdecent.cn/p/7db231163168
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容