Android App性能優(yōu)化技巧

當(dāng)出現(xiàn)App啟動慢、界面跳轉(zhuǎn)慢、事件相應(yīng)慢、滑動和動畫卡頓、展現(xiàn)內(nèi)容慢等問題的時候意味著App性能出現(xiàn)問題,這個時候就有必要對App做一下性能優(yōu)化,其實不是非等到App出現(xiàn)性能問題的時候才開始做優(yōu)化,而是要把優(yōu)化做到平時的開發(fā)和維護過程中。但性能優(yōu)化如何下手?下面結(jié)合Android官方文檔以及平時開發(fā)中的經(jīng)驗來聊聊Andrid性能優(yōu)化的技巧。

Layout布局優(yōu)化

Layout的優(yōu)化目的是處理應(yīng)用UI卡頓、ANR問題,使App用起來絲般順滑。

  • 優(yōu)化布局結(jié)構(gòu),避免復(fù)雜的Layout嵌套。比如使用<code>merge</code> 標(biāo)簽減少布局嵌套。
    使用 Hierarchy Viewer 分析布局,對出現(xiàn)紅色、或者黃色的布局需要尤為注意。
    Tree View
  • 使用Lint進行資源及冗余UI布局分析,根據(jù)Lint report提示做相應(yīng)的優(yōu)化
  • 使用<code>include</code> 標(biāo)簽達到布局重用的目的,
  • 使用懶加載的Views,比如ViewStub
  • 使用Memory監(jiān)測及GC打印與Allocation Tracker進行UI卡頓分析。
  • 使用RelativeLayout扁平化布局,ListView item布局,一定要扁平化。
  • TextView組合圖標(biāo),代替LinearLayout+TextView+ImageView。
  • 在項目中使用StrictMode來檢查主線程耗時操作等。
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().penaltyDialog().build());
 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
  • 自定義View時,避免onDraw方法過度繪制或者重復(fù)繪制,避免在里面初始化變量等。
  • ListView或者RecyclerView渲染item時,將耗時操作(文件,網(wǎng)絡(luò), DB)放在子線程中,item要是用ViewHolder布局緩存復(fù)用機制。
  • 使用traces.txt文件進行ANR分析優(yōu)化,或者使用Blockcanary,使用方法和Leackcanary類似。
  • 使用SVG代替圖片
  • 使用xml代替圖片

優(yōu)化設(shè)備的電池壽命

電池優(yōu)化一般只針對耗電量比較大的App,一般應(yīng)用貌似都還沒有考慮到這一點。對于電池使用優(yōu)化主要考慮:

  • 監(jiān)控電池電量和充電狀態(tài),監(jiān)控顯著的電池電量變化,一般而言,最好在電池電量極低時停用所有后臺更新。
  • 確定和監(jiān)控插接狀態(tài)和基座類型,具體參考官方文檔
  • 確定和監(jiān)控網(wǎng)絡(luò)連接狀態(tài),可以利用 ConnectivityManager 來檢查是否已實際連入互聯(lián)網(wǎng)以及已連入情況下的連接類型。

高效地利用線程

為了加快響應(yīng)速度,需要把費時的操作(比如網(wǎng)絡(luò)請求、數(shù)據(jù)庫操作或者復(fù)雜的計算)從主線程移動到一個單獨的線程中。

  • 可以使用自定義Thread,AsyncTask或者IntentService來創(chuàng)建后臺操作。
  • 可以使用線程池,并用創(chuàng)建一個Manager類對多線程統(tǒng)一管理
 private PhotoManager() {
        ...
        // Sets the amount of time an idle thread waits before terminating
        private static final int KEEP_ALIVE_TIME = 1;
        // Sets the Time Unit to seconds
        private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
        // Creates a thread pool manager
        mDecodeThreadPool = new ThreadPoolExecutor(
                NUMBER_OF_CORES,       // Initial pool size
                NUMBER_OF_CORES,       // Max pool size
                KEEP_ALIVE_TIME,
                KEEP_ALIVE_TIME_UNIT,
                mDecodeWorkQueue);
    }
  • 當(dāng)View銷毀或者不可見的情況下,線程有必要取消、停止, 通常在線程內(nèi)部設(shè)置flag取消耗時操作,盡快讓線程結(jié)束。
/*
 * Before continuing, checks to see that the Thread hasn't
 * been interrupted
 */
if (Thread.interrupted()) {
    return;
}

/*
 * isCanceled標(biāo)志
 */
if (isCanceled()) {
   return;
 }

優(yōu)化網(wǎng)絡(luò)

  • 如果沒有網(wǎng)絡(luò)連接,請讓你的應(yīng)用跳過網(wǎng)絡(luò)操作;只在有網(wǎng)絡(luò)連接并且無漫游的情況下更新數(shù)據(jù);
  • 選擇兼容的數(shù)據(jù)格式,把含有文本數(shù)據(jù)和二進制數(shù)據(jù)的請求全部轉(zhuǎn)化成二進制數(shù)據(jù)格式請求;
  • 使用高效的轉(zhuǎn)換工具,多考慮使用流式轉(zhuǎn)換工具,少用樹形的轉(zhuǎn)換工具;
  • 為了更快的用戶體驗,請減少重復(fù)訪問服務(wù)器的操作;
  • 如果可以的話,請使用framework的GZIP庫來壓縮文本數(shù)據(jù)以高效使用CPU資源。
    目前App針對網(wǎng)絡(luò)的優(yōu)化,主要使用一個好的網(wǎng)絡(luò)下載框架,目前OkHttp + Retrofit基本成為了標(biāo)配。

內(nèi)存泄露分析工具

  • 使用AS的Memory monitor,平時用來直觀了解自己應(yīng)用的全局內(nèi)存情況,大的泄露才能有感知。
  • DDMS-Heap內(nèi)存監(jiān)測工具 同上,大的泄露才能有感知。
  • MAT工具全稱為Memory Analyzer Tool,詳細分析Java堆內(nèi)存的工具,該工具非常強大。
  • Leakcanary神器,比較強大,可以感知泄露且定位泄露;實質(zhì)是MAT原理,只是更加自動化了,當(dāng)現(xiàn)有代碼量已經(jīng)龐大成型,且無法很快察覺掌控全局代碼時極力推薦;或者是偶現(xiàn)泄露的情況下極力推薦。

規(guī)避內(nèi)存泄露建議

  • Context使用不當(dāng)造成內(nèi)存泄露;不要對一個Activity Context保持長生命周期的引用(譬如上面概念部分給出的示例)。盡量在一切可以使用應(yīng)用ApplicationContext代替Context的地方進行替換(原理我前面有一篇關(guān)于Context的文章有解釋)。
  • 非靜態(tài)內(nèi)部類的靜態(tài)實例容易造成內(nèi)存泄漏;即一個類中如果你不能夠控制它其中內(nèi)部類的生命周期(譬如Activity中的一些特殊Handler等),則盡量使用靜態(tài)類和弱引用來處理(譬如ViewRoot的實現(xiàn))。
  • 警惕線程未終止造成的內(nèi)存泄露;譬如在Activity中關(guān)聯(lián)了一個生命周期超過Activity的Thread,在退出Activity時切記結(jié)束線程。一個典型的例子就是HandlerThread的run方法是一個死循環(huán),它不會自己結(jié)束,線程的生命周期超過了Activity生命周期,我們必須手動在Activity的銷毀方法中中調(diào)運thread.getLooper().quit();才不會泄露。
  • 對象的注冊與反注冊沒有成對出現(xiàn)造成的內(nèi)存泄露;譬如注冊廣播接收器、注冊觀察者(典型的譬如數(shù)據(jù)庫的監(jiān)聽)等。
  • 創(chuàng)建與關(guān)閉沒有成對出現(xiàn)造成的泄露;譬如Cursor資源必須手動關(guān)閉,WebView必須手動銷毀,流等對象必須手動關(guān)閉等。
  • 不要在執(zhí)行頻率很高的方法或者循環(huán)中創(chuàng)建對象,可以使用HashTable等創(chuàng)建一組對象容器從容器中取那些對象,而不用每次new與釋放。
  • 避免代碼設(shè)計模式的錯誤造成內(nèi)存泄露;譬如循環(huán)引用,A持有B,B持有C,C持有A,這樣的設(shè)計誰都得不到釋放。

代碼規(guī)范制定并遵守

一致的代碼風(fēng)格,有利于代碼維護、查看和發(fā)現(xiàn)問題所在。參考CodingStyle

其他

  • 使用Lint、 Proguard工具給APK包減肥。
  • 開啟GPU過度繪制調(diào)試,粉紅色盡量優(yōu)化,界面盡量保持藍綠顏色, 紅色肯定是有問題的,不能忍受。
  • 合理使用數(shù)據(jù)類型,比如StringBuilder代替String,少用父類聲明(List, Map), 盡量使用Android提供的數(shù)據(jù)結(jié)構(gòu)例如SparseArray。
  • 少用枚舉enum,可以使用static final的變量或者intDef 注解代替。
  • bitmap 必要時進行壓縮,降低分辨率處理,并且使用完了記得recycle。
  • 使用LruCache最Memory或者Disk Cache
  • OnTrimMemory 方法監(jiān)聽,收到內(nèi)存報警,及時釋放內(nèi)存。
  • 你要知道for loop中不要聲明臨時變量,不到萬不得已不要在里面寫try catch.
  • 設(shè)計模式合理的使用,不要一味的為了設(shè)計模式而過分的抽象代碼,因為代碼抽象系數(shù)與代碼加載執(zhí)行時間成正比
  • Handler發(fā)送消息時盡量使用obtain去獲取已經(jīng)存在的Message對象進行復(fù)用,而不是新new Message對象,這樣可以減輕內(nèi)存壓力。
  • 使用Parcelable代替Serializable,Parcelable更適合Android。

寫在最后

性能優(yōu)化是一個大的話題,設(shè)計到的知識太多太多,上面只是提到部分,后續(xù)會不但的更新,如有不對之處,還望指正。

參考

Android性能優(yōu)化的方方面面
那些Android中的性能優(yōu)化

最后編輯于
?著作權(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)容