1. 程式人生 > >Android效能優化總結

Android效能優化總結

1. 緣由

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

假設我們更新螢幕的背景圖片,需要24ms來做這次運算。當系統在第一個16ms時重新整理介面,然而我們的運算還沒有結束,無法繪出圖片。當系統隔16ms再發一次VSYNC資訊重繪介面時,使用者才會看到更新後的圖片。也就是說使用者是32ms後看到了這次重新整理(注意,並不是24ms),這就是丟幀。

大多數多用感知到卡頓等問題最主要的根源是渲染問題,而導致渲染問題的原因是效能問題,為了保證程式正常的使用,效能方面需要著重注意,本篇針對的效能優化是從一些平時常見的細節入手,

2. 效能優化

丟幀只是使用者能感知到的表面現象,嚴重的會引起程式卡頓甚至ANR,深層次的原因是程式碼中有比較耗時的操作阻塞到了主執行緒,也就是效能問題。

2.1 過度繪製

過度繪製(Overdraw)是指螢幕上同一個畫素點在同一幀時間內被繪製多次。在層級複雜的UI結構中,如果不可見的UI也被繪製,會導致某些畫素區被繪製多次,浪費大量的CPU以及GPU資源。

在手機的開發者選項中,開啟顯示佈局邊界即可檢視頁面的繪製資訊。


圖 1 - 佈局渲染檢視

有四中顏色,藍色,淡綠,淡紅,深紅分別代表著不同的繪製資訊。藍色代表一次繪製,淡綠代表兩次繪製,淡紅代表三次繪製,深紅代表四及以上次繪製。

我們的目的是儘量減少紅色的繪製資訊,方法有兩種。

  • 簡化頁面UI結構,複雜的UI佈局會導致大量View重疊,出現過度繪製的可能性比較大,要避免佈局巢狀過多,例如一般情況下,優先使用LinearLayout佈局。
  • 複用背景色,例如如果父佈局和子View背景色是相同的,只需要父佈局設定背景色即可,子View不用設定。

2.2 佈局優化

上面提到簡化佈局可以減少過度繪製的問題,佈局優化可以從下面幾方面入手。

  • 佈局的選擇,能滿足需求的情況下優先選擇LinearLayout,因為RelativeLayoutmeasure會比LinearLayout多出一次,
    即使LinearLayout
    使用了weight後,效能依然會比RelativeLayout
  • 邊距的設定,RelativeLayoutmeasure過程中,如果出現子View和佈局本身高度不同時候,還會觸發measure過程,解決方法很簡單,使用padding代替marigin
  • 複用佈局,include
  • 延遲載入,ViewStub
  • 合併佈局層級,merge

關於這塊的具體詳情,參考下面連結:

2.3 I/O操作(SharedPreferences)

這裡I/O操作僅針對SharedPreferences,因為這個基本是在Android中使用最多的儲存庫了。

2.3.1 讀操作

讀操作一般不會阻塞到主執行緒,如果讀取的資料比較大而且需要有大量的處理操作,直接開子執行緒,在子執行緒處理。

2.3.2 寫操作

寫操作比讀操作複雜一些,向SharedPreferences寫入資料時候,有commitapply兩個方法。

  • commit方法,直接將資料同步寫入磁碟
  • apply方法,先將資料寫入記憶體,再非同步寫入磁碟

commit和apply方法區別在於同步寫入和非同步寫入,以及是否需要返回值。在不需要返回值的情況下,使用apply方法可以極大的提高效能。

在使用lint靜態掃描程式碼時候,會建議使用apply去替代commit,說明官方也是支援使用apply方法的。

但是!!!

並不是是說apply不會阻塞到主執行緒,它也有可能會阻塞到主執行緒。詳情請看這篇文章SharedPreferences呼叫導致的ANR分析

長話短說。

使用apply方法時候,會向QueuedWork佇列中新增一個等待寫入操作完成的執行緒,只有當寫入操作完成後,才會從QueuedWork將等待執行緒執行緒移除掉。

而主執行緒中,Service的啟動和stop以及Activity的onPause()onStop()生命週期都會等待其他非同步執行緒完成,才會繼續執行。

例如停止Service的時候程式碼如下(ActivityThread類中):

private void handleStopService(IBinder token) {
    ···

    QueuedWork.waitToFinish(); // 等待其他非同步執行緒完成

    ···
}

可以看到,如果使用apply方法寫入大量複雜資料,確實有可能會阻塞到主執行緒,甚至可能導致ANR。

優化方法:

  • 複雜資料
    • 即時性較弱(距離下次使用時間較長),新建子執行緒使用commit方法,防止阻塞到主執行緒
    • 即時性較強,可以考慮直接放在記憶體中
  • 簡單資料,使用apply方法

2.4 序列化

大部分情況下,與後臺互動使用的資料是gson格式。相信很多是直接使用google官方的Gson類來序列化和反序列化的。

我在專案中就遇見了有使用Gson反序列化複雜資料時候,造成的卡頓現象。

Gson序列化和反序列化是可以優化的。

// 反序列化
public List<Message> readJsonStream(InputStream in) throws IOException {
    JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
    List<Message> messages = new ArrayList<Message>();
    reader.beginArray();
    while (reader.hasNext()) {
        Message message = gson.fromJson(reader, Message.class);
        messages.add(message);
    }
    reader.endArray();
    reader.close();
    return messages;
}

// 序列化
public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
    JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
    writer.setIndent("  ");
    writer.beginArray();
    for (Message message : messages) {
        gson.toJson(message, Message.class, writer);
    }
    writer.endArray();
    writer.close();
}

2.5 反射

大部分框架或者SDK為了解耦,或多或少的會使用到反射,反射本身會效能就會比直接呼叫差很多。

有時候會大批量使用反射去例項化物件,所以不要在那些會被反射例項化的類的建構函式中做太多的事情。

建議在獲取例項化物件後,再使用物件直接呼叫初始化方法。

2.6 異常

在程式碼中有時候可能會發生異常,所以一般使用try/catch來做保護,避免程式崩潰。

一旦有異常發生,系統會耗費資源去處理,本身對記憶體和CPU就會有消耗。

所以不能因為有了try/catch而不顧程式碼質量,要儘量保證沒有大量的異常出現,我在專案中見過大量空指標異常出現,這種異常我們可以在程式碼層面就直接避免。

2.7 頻繁GC

頻繁的GC也會對效能造成影響,嚴重的會導致卡頓或者ANR。

頻繁GC原因有兩個

  • 記憶體抖動,大量的物件被建立又在短時間內馬上被釋放
  • 瞬間產生大量的物件會嚴重佔用Young Generation的記憶體區域,當達到閥值,剩餘空間不夠的時候,也會觸發GC。即使每次分配的物件佔用了很少的記憶體,但是他們疊加在一起會增加 Heap的壓力,從而觸發更多其他型別的GC。

在專案中避免短時間內突然建立大量的物件。

2.8 裝置&應用基本資訊

獲取裝置&應用基本資訊,比如說包名、IMEI資訊等等,是比較耗時的操作,可以進行一些優化。

  • 固定資訊獲取採用快取策略,例如版本號、版本名稱、手機mac地址、運營商資訊等等,一次獲取,快取到記憶體,下次直接使用
  • 不部分不固定資訊,例如網路情況(2G/3G/4G/WIFI),可以採用監聽網路變更方式,來即時更新網路情況
  • 其他不固定資訊,比如基站資訊,只能每次獲取

2.9 待續

目前自己遇見並優化過的都在以上列出來了,後續會慢慢積累。

3. 總結

關於效能優化方面知識,官方也出過視訊Android Performance Patterns,網上也有很多翻譯的文章,寫得都比較詳細,這篇文章是自身從平時一些常見點入手做的一些總結,希望對大家有幫助。

相關推薦

自己的Android效能優化總結

前言: 之前效能優化相關的學習都是靠查資料學,效能優化相關的內容挺多的,自己來做個總結吧!有時間自己就把之前寫的筆記整理一點,一點一點積累。 apk瘦身 使用progard 使用webp圖片 使用向量圖 移除未使用的資源 儘量使用系統資源

Android效能優化總結

1. 緣由 Android系統每隔16ms發出VSYNC訊號,對UI進行渲染,如果每次渲染都成功,就能夠達到流暢的畫面所需要的60fps,為了能夠實現60fps,這意味著程式的大多數操作都必須在16ms內完成,時間超出16ms越多,丟的幀就越多。 假設我

Android 效能優化總結和彙總

對於Android開發者而言,開發出一個應用程式已經不是很難的問題。但是,如果要想開發出優質的應用或者程式就需要知道如何優化應用以及對程式效能優化的一些知識。那麼如何去優化程式呢?安卓框架已經提供了很好的工具來幫助開發者做這件事情。廢話少說,進入正題。 A.

Android 效能優化總結

6、合理選擇容器,在效能上優先考慮陣列,即使我們現在習慣了使用容器,也要注意頻繁使用容器在效能上的隱患點:首先是擴容開銷, HashMap擴容時重新Hash的開銷較大。其次是記憶體開銷,HashMap需要額外的Map.Entry物件分配 ,需要額外記憶體,也容易產生更多的記憶體碎片。SparseArray和

Android效能優化----經典總結

Android 效能優化典範(一):主要從 Android 的渲染機制、記憶體與 GC、電量優化三個方面展開,介紹了 Android 中效能問題的底層工作原理,以及如何通過工具來找出效能問題及提升效能的建議。 Android 效能優化典範(二):主要內容為:電量優化、網路優化、Android W

Android效能優化——如何避免OOM總結

從四個方面著手,首先是減小物件的記憶體佔用,其次是記憶體物件的重複利用,然後是避免物件的記憶體洩露,最後是記憶體使用策略優化。 減小物件的記憶體佔用 避免OOM的第一步就是要儘量減少新分配出來的物件佔用記憶體的大小,儘量使用更加輕量的物件。 1)使用更加輕

關於Android效能優化的簡單總結

Android效能優化 Android效能優化主要分幾大類:1。app啟動優化  2.佈局優化   3. 響應優化    4.記憶體優化   5.網路優化      一。效能分析工具     1。Hierarchy Viewer提供了一個視覺化的介面來檢測佈局的層級,讓我

Android開發效能優化總結(一)

安卓開發應用首先要講究良好的使用者體驗,如果一款軟體卡頓現象嚴重,不流暢,經常崩潰,那麼將給使用者帶來極不良好的體驗,從而損失使用者。 在實際開發和學習中,我總結了一下關於安卓效能的優化,供大家參考交流。 應用程式的效能問題體現在很多方面, 比如第一次啟動速

Android——效能優化之SparseArray

相信大家都用過HashMap用來存放鍵值對,最近在專案中使用HashMap的時候發現,有時候 IDE 會提示我這裡的HashMap可以用SparseArray或者SparseIntArray等等來代替。 SparseArray(稀疏陣列).它是Android內部特有的api,標準的jdk是沒有這

Android效能優化之較精確的獲取影象顯示到螢幕上的時間

轉載自:http://blog.desmondyao.com/android-show-time/ 這兩天我的包工頭歪龍木·靈魂架構師·王半仙·Yrom給我派了一個活:統計App冷啟動時間。這個任務看上去不難,但是要求統計出來的時間要準,要特別準。 意思就是,我必須要按Activity繪製到

C程式碼效能優化總結

轉自:https://blog.csdn.net/chenyq991/article/details/79047741 1、優化程式碼框架 個人覺得程式碼架構對效能的影響至關重要,就好骨架之於人,所以我把這個放在第一點。舉個簡單的例子: 優化前: void main() { whi

Android效能優化——介面流暢度優化

Android效能優化——介面流暢度優化   序言 首先流暢度不僅僅是受到程式碼的影響。也會跟機器的硬體配置有關係。所以第一點需要明確的是,流暢度最低保證在哪個硬體配置之上。這樣有了一個基點之後,才能比較好明確優化目標。不然你拿一個兩三年前的機子來做優化。那就真的是吃力不討好的

TOMCAT7併發效能優化總結

最近由於工作需要看了很多tomcat效能優化的資料,在此記錄總結一下,以備日後之需。 總結起來其實有三點: 一、tomcat啟動JVM引數調優 具體做法為在catalina.bat前面加上JAVA_OPTS引數設定 set JAVA_OPTS= -server #以伺服

【HBase調優】Hbase萬億級儲存效能優化總結

背景:HBase主叢集在生產環境已穩定執行有1年半時間,最大的單表region數已達7200多個,每天新增入庫量就有百億條,對HBase的認識經歷了懵懂到熟的過程。為了應對業務資料的壓力,HBase入庫也由最初的單機多執行緒升級為有容災機制的分散式入庫,為及早發現叢集中的問題,還開發了一套對HBas

Android效能優化之圖片壓縮優化

1 分類Android圖片壓縮結合多種壓縮方式,常用的有尺寸壓縮、質量壓縮、取樣率壓縮以及通過JNI呼叫libjpeg庫來進行壓縮。 參考此方法:Android-BitherCompress 備註:對於資源圖片直接使用:tiny壓縮 2 質量壓縮(1)原理:保持畫素的前提下改變圖片的位深及透明度,(即:通

SQL 效能優化 總結

SQL 效能優化 總結  (1)選擇最有效率的表名順序(只在基於規則的優化器中有效):         ORACLE的解析器按照從右到左的順序處理FROM子句中的表名,FROM子句中寫在最後的表(基礎表 driving table)

Unity3D效能優化總結

轉自https://www.cnblogs.com/quansir/p/6370796.html 一、程式方面    01、務必刪除指令碼中為空或不需要的預設方法;    02、只在一個指令碼中使用OnGUI方法;    03、避免在OnGUI中對變數、方法進行更新、賦值,輸出變數建

MySQL效能優化總結___本文乃《MySQL效能調優與架構設計》讀書筆記!

一、MySQL的主要適用場景 1、Web網站系統 2、日誌記錄系統 3、資料倉庫系統 4、嵌入式系統 二、MySQL架構圖:   三、MySQL儲存引擎概述 1)MyISAM儲存引擎 MyISAM儲存引擎的表在資料庫中,每一個表

35 個 Java 程式碼效能優化總結(一)

前言 程式碼優化,一個很重要的課題。可能有些人覺得沒用,一些細小的地方有什麼好修改的,改與不改對於程式碼的執行效率有什麼影響呢?這個問題我是這麼考慮的,就像大海里面的鯨魚一樣,它吃一條小蝦米有用嗎?沒用,但是,吃的小蝦米一多之後,鯨魚就被餵飽了。程式碼優化也是一樣,如果專案著眼於儘快無BUG

Android效能優化—不建議使用列舉Enum

最近優化App,由於專案中使用了Lib,而Lib程式碼中包含了大量的列舉型別,導致App佔用記憶體過多。好吧,知道問題點,那就幹掉,拋棄之~ 問題是解決了,為啥會這樣呢? 先來看看Android官網的說明吧: 看見了吧,Android官網不建議咱們使用enums,說的也很清楚了,佔用記憶體多(E