Android性能優化的淺談

来源:http://www.cnblogs.com/vincent0519/archive/2016/11/14/6060114.html
-Advertisement-
Play Games

一、概要: 本文主要以Android的渲染機制、UI優化、多線程的處理、緩存處理、電量優化以及代碼規範等幾方面來簡述Android的性能優化 二、渲染機制的優化: 大多數用戶感知到的卡頓等性能問題的最主要根源都是因為渲染性能。 Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染, ...


一、概要:

    本文主要以Android的渲染機制、UI優化、多線程的處理、緩存處理、電量優化以及代碼規範等幾方面來簡述Android的性能優化

 

二、渲染機制的優化:

    大多數用戶感知到的卡頓等性能問題的最主要根源都是因為渲染性能

    Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染, 如果每次渲染都成功,這樣就能夠達到流暢的畫面所需要的60fps,為了能夠實現60fps,這意味著程式的大多數操作都必須在16ms內完成。

    

    如果你的某個操作花費時間是24ms,系統在得到VSYNC信號的時候就無法進行正常渲染,這樣就發生了丟幀現象。那麼用戶在32ms內看到的會是同一幀畫面。

    

    Probably:也許是因為你的layout太過複雜,無法在16ms內完成渲染,有可能是因為你的UI上有層疊太多的繪製單元,還有可能是因為動畫執行 的次數過多。這些都會導致CPU或者GPU負載過重。

    Resolved:我們可以通過一些工具來定位問題,比如可以使用HierarchyViewer來查找Activity中的佈局是否過於複雜,也可以使用手機設置里 面的開發者選項,打開Show GPU Overdraw等選項進行觀察。

          你還可以使用TraceView來觀察CPU的執行情況,更加快捷的找到性能瓶頸。

    

    淺談Overdraw(過度繪製):

      Overdraw(過度繪製)描述的是屏幕上的某個像素在同一幀的時間內被繪製了多次。在多層次的UI結構裡面,如果不可見的UI也在做繪製的操作,這就會導致某些像素區域被繪製了多次。這就浪費大量的CPU以及GPU資源。 

      

      Tips:我們可以通過手機設置裡面的開發者選項,打開Show GPU Overdraw的選項,可以觀察UI上的Overdraw情況

      

      圖解:藍色,淡綠,淡紅,深紅代表了4種不同程度的Overdraw情況,我們的目標就是儘量減少紅色Overdraw,看到更多的藍色區域。

      Tips:Overdraw有時候是因為你的UI佈局存在大量重疊的部分,還有的時候是因為非必須的重疊背景。例如某個Activity有一個背景,然後裡面 的Layout又有自己的背景,同時子View又分別有自己的背景。

      僅僅是通過移除非必須的背景圖片,這就能夠減少大量的紅色Overdraw區域,增加 藍色區域的占比。這一措施能夠顯著提升程式性能。

 

      註意:如需獲取更多渲染機制的內容,請移步 https://www.oschina.net/news/60157/android-performance-patterns

 

三、UI方面的優化:

     1)首先簡單談談view的繪製流程:measure - layout - draw

     

      ps:具體的流程網上一搜一大把+_+

     2)子控制項越多,繪製的時間也就越長。

      對於Listview或者GridView這種多item的組件來說,復用item可以減少inflate次數,通過setTaggetTagViewHolder方式實現復用,這裡要註意的是,holder中的控制項最好reset後再賦值,避免圖片,文字錯亂。

      *以下簡單的例子:(儘量使用註解,有很多註解的開源框架可以使用:butterKnife, AndroidAnnotation, Dragger2

 1 static class ViewHolder{
 2     @InjectView(R.id.imageView1)
 3     ImageView imageView1;
 4     @InjectView (R.id.text1)
 5     TextView textView1;
 6 }
 7 
 8 @Override
 9 public View getView(int position, View convertView, ViewGroup parent) {
10     ViewHolder holder;
11              
12     if(convertView == null){
13        holder = new ViewHolder();
14        convertView = LayoutInflater.from(mContext).inflate(R.layout.listview_item, null);
15        convertView.setTag(holder);
16      }else{
17          holder = (ViewHolder)convertView.getTag();
18      }
19              
20          holder.imageView1.setImageResource(R.drawable.ic_launcher);
21          holder.textView1.setText(mData.get(position));  
22              
23          return convertView;
24 }

    3)UI ReView(視圖的檢查) 

      1. 減少視圖層級可以有效的減少記憶體消耗,因為視圖是一個樹形結構,每次刷新和渲染都會遍歷一次。

      2. 想要減少視圖層級首先就需要知道視圖層級,所以下麵介紹一個SDK中自帶的一個非常好用的工具hierarchyviewer。你可以在下麵的地址找到它:your sdk path\sdk\tools

      

      3. 如上圖大家可以看到,hierarchyviewer可以非常清楚的看到當前視圖的層級結構,並且可以查看視圖的執行效率(視圖上的小圓點,綠色表示流暢,黃色和紅色次之),所以我們可以很方便的查看哪些view可能會影響我們的性能從而去進一步優化它。

       hierarchyviewer還提供另外一種列表式的查看方式,可以查看詳細的屏幕畫面,具體到像素級別的問題都可以通過它發現。

       

      4)一些標簽的使用

      <merge>它在優化UI結構時起到很重要的作用。目的是通過刪減多餘或者額外的層級,從而優化整個Android Layout的結構,優化佈局層數。

      <include >可以通過這個標簽直接載入外部的xml到當前結構中,是復用UI資源的常用標簽,來共用佈局。

      <ViewStub>動態載入view,此標簽可以使UI在特殊情況下,直觀效果類似於設置View的不可見性,但是其更大的意義在於被這個標簽所包裹的Views在預設狀態下不會占用任何記憶體空間。

 

四、多線程的處理:

   1. Android 提供的多種多線程工具類 (AsyncTask, HandlerThread, IntentService, ThreadPool),許多操作都需要由 主線程(UI 線程)來執行,比如:

      

      2. Android 系統的屏幕刷新頻率為 60 fps, 也就是每隔 16 ms 刷新一次。如果在某次繪製過程中,我們的操作不能在 16 ms 內完成,那它則不能趕上這次的繪製公交車,只能等下一輪。

       這種現象叫做 “掉幀”,用戶看到的就是界面繪製不連續、卡頓。

      

      3. HandlerThread 就是MessageQueue,Looper和 Handler 的組合。每個應用啟動時,系統會創建一個該應用的進程以及主線程,這裡的主線程就是一個 HandlerThread。

      

      4. 註意問題:

       1)多線程併發訪問資源要遵循重要的原則就是 原子性、可見性、有序性。沒有同步機制的情況下,多個線程同時讀寫記憶體可能會導致意料之外的問題:

       線程安全的問題; ② UI 組件的生命周期並不確定;線程引用導致的記憶體泄漏問題

          2)不要在任何子線程持有 UI 組件或者 Activity 的引用

       3)保持響應不發生ANR: 

        ①從UI線程中移除費時操作這個方式還可以防止用戶操作出現系統不響應(ANR)對話框。需要做的就是繼承AsyncTask來創建一個後臺工作線程,並實現doInBackground()方法。

        ②還有一種方式就是自己創建一個Thread類或者HandlerThread類,明確設定線程的優先順序。

       4)使用StrictMode來檢查UI線程中可能潛在的費時操作,使用一些特殊的工具如Safe.ijiamiSystrace或者Traceview來尋找在你的應用中的瓶頸;用進度條向用戶展示操作進度。

 

五、緩存處理:

  簡單的說說緩存優化的幾個方面:

    緩存機制網路+資料庫。為了避免從網路獲取重覆的數據,可以在activity或者fragment或者每個組件設置一個最大請求間隔。

        比如一個listview,第一次請求數據時,保存一份到資料庫,並記下時間戳,當下次重新初始化時,判斷是否超過最大時間間隔(5分鐘),如果沒有,只載入資料庫數據,不需要再做網路請求。

        (當然,還有一些隱式的http請求框架會緩存伺服器數據,在一定時間內不再請求網路,或者當伺服器返回304時將之前緩存的數據直接返回)

 

 

   網路方面:1)需要服務端配合的:json數據格式,WebP代替jpg,支持斷點續傳,多個請求合併成一個,儘量不做重定向,伺服器緩存以及負載均衡等。

        2)對客戶端本身,除了上述的實現,我們還需要合理的緩存,控制最大請求併發量,及時取消已失效的請求,過濾重覆請求,timeout時間設置,請求優先順序設置等。

         

        * WebP:WebP 的優勢體現在它具有更優的圖像數據壓縮演算法,能帶來更小的圖片體積,而且擁有肉眼識別無差異的圖像質量;

             同時具備了無損和有損的壓縮模式、Alpha 透明以及動畫的特性,在 JPEG PNG 上的轉化效果都相當優秀、穩定和統一。

             

               轉換後的 WebP 支持 Alpha 透明和 24-bit 顏色數,不存在 PNG8 色彩不夠豐富和在瀏覽器中可能會出現毛邊的問題。

              * 更多瞭解請看:http://isux.tencent.com/introduction-of-webp.html

 

 

 六、電量優化:

     有下麵一些措施能夠顯著減少電量的消耗:

      • 我們應該儘量減少喚醒屏幕的次數與持續的時間,使用WakeLock來處理喚醒的問題,能夠正確執行喚醒操作並根據設定及時關閉操作進入睡眠狀態。

      • 某些非必須馬上執行的操作,例如上傳歌曲,圖片處理等,可以等到設備處於充電狀態或者電量充足的時候才進行。

      • 觸髮網絡請求的操作,每次都會保持無線信號持續一段時間,我們可以把零散的網路請求打包進行一次操作,避免過多的無線信號引起的電量消耗。

        關於網路請求引起無線信號的電量消耗,還可以參考這裡http://hukai.me/android-training-course-in-chinese/connectivity/efficient-downloads/efficient-network-access.html

    

 

    Ask:假設你的手機裡面裝了大量的社交類應用,即使手機處於待機狀態,也會經常被這些應用喚醒用來檢查同步新的數據信息。

    Android會不斷關閉各種硬 件來延長手機的待機時間,首先屏幕會逐漸變暗直至關閉,然後CPU進入睡眠,這一切操作都是為了節約寶貴的電量資源。但是即使在這種睡眠狀態下,大多數應 用還是會嘗試進行工作,他們將不斷的喚醒手機。

    一個最簡單的喚醒手機的方法是使用PowerManager.WakeLock的API來保持CPU工作並 防止屏幕變暗關閉。這使得手機可以被喚醒,執行工作,然後回到睡眠狀態。知道如何獲取WakeLock是簡單的,可是及時釋放WakeLock也是非常重 要的。

    不恰當的使用WakeLock會導致嚴重錯誤。例如網路請求的數據返回時間不確定,導致本來只需要10s的事情一直等待了1個小時,這樣會使得電量 白白浪費了。這也是為何使用帶超時參數的wakelock.acquice()方法是很關鍵的。

    但是僅僅設置超時並不足夠解決問題,例如設置多長的超時比 較合適?什麼時候進行重試等等?

 

    Resolved:解決上面的問題,正確的方式可能是使用非精准定時器。通常情況下,我們會設定一個時間進行某個操作,但是動態修改這個時間也許會更好。

    例如,如果有 另外一個程式需要比你設定的時間晚5分鐘喚醒,最好能夠等到那個時候,兩個任務捆綁一起同時進行,這就是非精確定時器的核心工作原理。我們可以定製計劃的 任務,可是系統如果檢測到一個更好的時間,它可以推遲你的任務,以節省電量消耗。

    

    * 關於JobScheduler的更多知識可以參考 http://hukai.me/android-training-course-in-chinese/background-jobs/scheduling/index.html

 

 七、代碼規範

  

    1for loop中不要聲明臨時變數,不到萬不得已不要在裡面寫try catch

    2)明白垃圾回收機制,避免頻繁GC,記憶體泄漏,OOM(有機會專門說)

    3)合理使用數據類型,StringBuilder代替String,少用枚舉enum,少用父類聲明(List,Map)

    4)如果你有頻繁的new線程,那最好通過線程池去execute它們,減少線程創建開銷。

    5)你要知道單例的好處,並正確的使用它。

    6)多用常量,少用顯式的"action_key",並維護一個常量類,別重覆聲明這些常量。

    7)如果可以,至少要弄懂設計模式中的策略模式,組合模式,裝飾模式,工廠模式,觀察者模式,這些能幫助你合理的解耦,即使需求頻繁變更,你也不用害怕牽一發而動全身。需求變更不可怕,可怕的是沒有在寫代碼之前做合理的設計。

    8)View中設置緩存屬性.setDrawingCachetrue.

    9)cursor 的使用。不過要註意管理好cursor,不要每次打開關閉cursor.因為打開關閉Cursor非常耗時。 Cursor.require用於刷cursor.

    10)採用SurfaceView在子線程刷新UI, 避免手勢的處理和繪製在同一UI線程(普通View都這樣做)

    11)採用JNI,將耗時間的處理放到c/c++層來處理

    12)有些能用文件操作的,儘量採用文件操作,文件操作的速度比資料庫的操作要快10倍左右

    13)懶載入和緩存機制。訪問網路的耗時操作啟動一個新線程來做,而不要再UI線程來做

    14)如果方法用不到成員變數,可以把方法申明為static,性能會提高到15%20%

    15)避免使用getter/setter存取field,可以把field申明為public,直接訪問

    16)私有內部類要訪問外部類的field或方法時,其成員變數不要用private,因為在編譯時會生成setter/getter,影響性能。可以把外部類的field或方法聲明為包訪問許可權

    17)合理利用浮點數,浮點數比整型慢兩倍

    18)針對ListView的性能優化,ListView的背景色與cacheColorHint設置相同顏色,可以提高滑動時的渲染性能。ListViewgetView是性能是關鍵,這裡要儘可能的優化。

       getView方法中要重用viewgetView方法中不能做複雜的邏輯計算,特別是資料庫操作,否則會嚴重影響滑動時的性能

    19)不用new關鍵詞創建類的實例,用new關鍵詞創建類的實例時,構造函數鏈中的所有構造函數都會被自動調用。但如果一個對象實現了Cloneable介面,我們可以調用它的clone()方法。

      clone()方法不會調用任何類構造函數。在使用設計模式(Design Pattern)的場合,如果用Factory模式創建對象,則改用clone()方法創建新的對象實例非常簡單。例如,下麵是Factory模式的一個典型實現:

    20)public static Credit getNewCredit() {
            return new Credit();
      }
      改進後的代碼使用clone()方法,如下所示:
      private static Credit BaseCredit = new Credit();
            public static Credit getNewCredit() {
                  return (Credit) BaseCredit.clone();
            }
    上面的思路對於數組處理同樣很有用。

    21)乘法和除法

      考慮下麵的代碼:

    1. for (val = 0; val < 100000; val +=5) { alterX = val * 8; myResult = val * 2; }
      用移位操作替代乘法操作可以極大地提高性能。下麵是修改後的代碼:
      for (val = 0; val < 100000; val += 5) { alterX = val << 3; myResult = val << 1; }

    22)ViewPager同時緩存page數最好為最小值3,如果過多,那麼第一次顯示時,ViewPager所初始化的pager就會很多,這樣pager累積渲染耗時就會增多,看起來就卡。

    23)每個pager應該只在顯示時才載入網路或資料庫(UserVisibleHint=true),最好不要預載入數據,以免造成浪費

     24)提高下載速度:要控制好同時下載的最大任務數,同時給InputStream再包一層緩衝流會更快(BufferedInputStream)

    25)提供載入速度:讓服務端提供不同解析度的圖片才是最好的解決方案。還有合理使用記憶體緩存,使用開源的框架

 

    


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在CSS3中提供了對邊框進行圓角設定的支持,可對邊框1~4個角進行圓角樣式設置。 ...
  • 1、SVG繪圖總結: ①方法一:已有svg文件,<img src="x.svg"> 方法二:<body><svg></svg></body> ②繪製矩形:<rect x="" y="" width="" height=""></rect> ③繪製圓形:<circle cx="" cy="" r="" ...
  • jquery美化選擇器實例有:邊框、下劃線、 伸縮 、滑動、 覆蓋、 旋轉、 彈出層選擇 、環形效果。 線上預覽 實例代碼 <body class="demo-1"> <div class="container"> <header class="codrops-header"> <h1> 自定義選擇 ...
  • //創建新的tcp伺服器var net = require('net');var chatServer = net.createServer()chatServer.on('connection',function(client){ client.write('Hi\n'); client.writ ...
  • 先上效果圖: 獲取手機已安裝的App列表利用Android系統API就可以辦到,這裡為什麼要引入RxJava?現在我們假設一下有下麵幾個需求: 1、我們不需要所有的App,只需要用戶安裝的第三方App,即過濾到系統App; 2、我們自定義一個AppInfo類,該類中保存了App_Icon、App_N ...
  • 今天學到了這個Loader,淺談一下自己的看法: 1.定義 Loader是一個載入器,可以用來它訪問數據,可以看做訪問數據的機器(好比挖掘機)。裝再器從android3.0開始引進,它使得在activity或fragment中非同步載入數據變得簡單。 具有如下特別: 1)它們對每個Activity和F ...
  • 轉載請標明出處:http://www.cnblogs.com/zhaoyanjun/p/6062880.html 本文出自 "【趙彥軍的博客】" 前言 以前我在 "【Android Handler、Loop 的簡單使用】" 介紹了子線程和子線程之間的通信。 很明顯的一點就是,我們要在子線程中調用Lo ...
  • 通常我們在處理耗時任務時候都會通過新建線程來處理,當任務處理完後通過Handler將結果發送回主線程。比如下麵示例: 那麼,我們能不能通過Handler從主線程發送消息給子線程呢?答案是肯定的,需要用到Looper.prepare()和Looper.loop()。如下麵的代碼: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...