第十章 Android性能優(yōu)化

Android 群英傳筆記
第一章Android體系與系統(tǒng)架構(gòu)
第二章 Android開發(fā)工具及技巧
第三章 Android控件架構(gòu)與事件攔截機制
第四章 ListView 使用技巧
第五章 Android Scroll 分析
第六章 Android 繪圖機制與屏幕適配
第七章 Android 動畫機制與使用技巧
第八章 Activity與Activity調(diào)用棧分析
第九章 Android 系統(tǒng)信息與安全機制
第十章 Android性能優(yōu)化
本文出自:
http://m.itdecent.cn/u/a1251e598483

本章的主要內(nèi)容:

1. 布局優(yōu)化

2. 內(nèi)存優(yōu)化

3. 使用各種工具進(jìn)行分析,優(yōu)化

1. 布局優(yōu)化

系統(tǒng)在渲染圖片的時候需要消耗大量的資源,一個好的UI,不僅要有良好的視覺體驗,更應(yīng)該具有良好的使用體驗,因此布局優(yōu)化就顯得很重要了

Android UI渲染機制

人眼所看到的流暢畫面,需要畫面的幀數(shù)達(dá)到40幀每秒到60幀每秒,相信玩過PC游戲的都應(yīng)該對幀有一個詳細(xì)的概念,最佳的ftp在60左右,這也是評價一個顯卡性能的一個高低標(biāo)準(zhǔn)之一,在Android中,系統(tǒng)通過VSYNC信號出發(fā)對UI的渲染、重繪,其間隔時間是16ms。這個16ms其實就是1000ms中顯示60幀畫面的單位時間。即1000/60,如果系統(tǒng)每次渲染都保持在16ms之內(nèi),那么我們看到的UI將十分的流暢,但這也是需要將所有的邏輯都保證在16ms里,如果16ms不能完成繪制,那么就會造成丟幀的現(xiàn)象,即當(dāng)前該重繪的幀被未完成的邏輯阻塞

Android系統(tǒng)提供了檢測UI渲染時間的工具,打開“開發(fā)者選項”,選擇“Profile GPU Rendering”(我的手機是“GPU呈現(xiàn)模式分析”),選中“On screen as bars”(我的為“在屏幕上顯示為條形圖”)。每一條柱狀線都包括三部分,藍(lán)色代表測量繪制Display List的時間,紅色代表OpenGL渲染Display List所需要的時間,黃色代表CPU等待GPU處理的時間,中間綠色橫線代表VSYNC時間16ms,需要盡量將所有條形圖都控制在這條綠線之下。

避免Overdraw

Overdraw,過渡繪制會浪費很多CPU、GPU資源,例如系統(tǒng)默認(rèn)會繪制Activity的背景,而如果再給布局繪制了重疊的背景,那么默認(rèn)Activity的背景就屬于無效的過渡繪制。Android系統(tǒng)在開發(fā)者選項中提供了這樣一個檢測工具–“Enable GPU Overdraw”。借助它可以判斷Overdraw的次數(shù)。盡量增大藍(lán)色的區(qū)域,減少紅色的區(qū)域,這里,我們用一張Google開發(fā)者博客上的圖片來表示


Enable GPU Overdraw

通過這個工具可以查看當(dāng)前區(qū)域中繪制的次數(shù),從而盡量優(yōu)化繪圖層次,盡量增大藍(lán)色的區(qū)域,減少紅色的區(qū)域

優(yōu)化布局層級

在Android中系統(tǒng)對View的測量、布局和繪制都是通過遍歷View樹來進(jìn)行的,如果View樹太高,就會影響其速度,因此優(yōu)化布局的第一個方法就是減低view樹的高度,Google也在其api文檔中建議view樹的結(jié)構(gòu)不宜超過十層

不知道是否讀者有注意到,在早期的Android版本中,Google使用線性布局作為默認(rèn)布局,而在現(xiàn)在的Android使用的是相對布局(默認(rèn)),原因就是通過扁平的相對布局來降低通過線性布局所產(chǎn)生的樹的高度,從而提高布局的效率

避免嵌套過多無用的布局

嵌套的布局會讓view樹的高度越來越高,所以在布局中,需要根據(jù)自身布局的特點,來選擇不同的layout組件,從而避免通過某一種layout組件來實現(xiàn)功能時的局限性,從而造成嵌套過多的情況

    1. 使用< include>標(biāo)簽重用布局

在一個應(yīng)用程序里,為了風(fēng)格和是哪個的統(tǒng)一,很多界面都會存在一些共同的UI,比如toolbar什么的,我們就可以使用< include>標(biāo)簽重用布局

    1. 使用< ViewStub>實現(xiàn)view的延遲加載

除了把一個view作為公用的ui,還可以對他進(jìn)行延時加載,用< ViewStub>就可以輕松實現(xiàn),< ViewStub>是一個輕量級的組件,他不僅不可視,而且大小為0,我們用小案例說話

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="not often use" />
</RelativeLayout>

然后使用ViewStub

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ViewStub
        android:id="@+id/viewStub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout="@layout/activity_main" />
</RelativeLayout>

運行之后,我們發(fā)現(xiàn)引用的組件確實沒有加載出來,那要怎么去加載尼?,同樣的,我們需要找控件

ViewStub viewStub = (ViewStub) findViewById(R.id.viewStub);

通過調(diào)用ViewStub的setVisibility()方法來顯示這個view ,也可以 通過調(diào)用ViewStub的inflate方法來顯示這個view

viewStub.setVisibility(View.VISIBLE);
View inflateView = viewStub.inflate();

這兩種方式都可以讓ViewStub重新展開,顯示引用的布局,而唯一的區(qū)別就是inlalte方法可以返回引用的布局

這里可能有同學(xué)有疑問了, ViewStub標(biāo)簽和設(shè)置View.GONE有什么區(qū)別,的確,他們兩個剛開始都不會加載,但是ViewStub只會顯示的時候,才去渲染這個布局,而后者不是,這就是優(yōu)勢

Hierarchy Viewer

無論哪本將優(yōu)化的書,他們都不得不提到Hierarchy Viewer,Android Studio支持這個功能,可以在tools -->android-->android Device Monitor 中打開

2. 內(nèi)存優(yōu)化

應(yīng)用App內(nèi)存的使用,也是評價一個應(yīng)用性能高低的一個重要指標(biāo)。雖然現(xiàn)在智能手機的內(nèi)存越來越大,但是一個好的應(yīng)用應(yīng)當(dāng)將效率發(fā)揮到極致,精益求精。而現(xiàn)在有很多應(yīng)用, 為了自己的利益,使用一些非常影響系統(tǒng)效率的方法, 不僅敗壞了Androld的口碑, 更極大地影響了系統(tǒng)的穩(wěn)定性,例如某 “X米”團(tuán)購應(yīng)用,在啟動應(yīng)用的時候會fork一個子線程, 用于監(jiān)聽用戶卸載應(yīng)用。在KK下, 該線程在卸載時不能被kill,且每次啟
動都將fork新的進(jìn)程;這就導(dǎo)致內(nèi)存消耗不斷增高, 極大地影響了低端機的使用體驗,因此,不管是什么應(yīng)用,都應(yīng)該把內(nèi)存效率,用戶體驗放在首位,而不是為了滿足自己的利益

什么是內(nèi)存

由于Android的沙箱機制,每個應(yīng)用所分配的內(nèi)存大小是有限度的,內(nèi)存太低就會觸發(fā)LMK-Low Memory Killer機制。那么到底什么是內(nèi)存呢?通常情況下我們所說的內(nèi)存是指手機的RAM,它包括以下幾個部分

Java 內(nèi)存劃分
  • 寄存器(Registers)

速度最快的存儲場所,因為寄存器位于處理器內(nèi)部.在程序中無法控制

  • 棧(Stack)

存放基本類型的數(shù)據(jù)和對象的引用.但對像本身不存放在棧中,而是存放在堆中

  • 堆內(nèi)存(Heap)

堆內(nèi)存用來存放由new創(chuàng)建的對象和數(shù)組.在堆中分配的內(nèi)存,由java虛擬機的自動垃圾回收(GC)管理

  • 靜態(tài)存儲區(qū)域(static Field)

靜態(tài)存儲區(qū)域就是指在固定的位置存放應(yīng)用程序運行時一直存在的數(shù)據(jù),java在內(nèi)存中專門劃分了一個靜態(tài)存儲區(qū)域來管理一些特殊的 數(shù)據(jù)變量如靜態(tài)的數(shù)據(jù)變量

  • 常量池(Constant Pool)

JVM虛擬機必須為每個被裝載的類型維護(hù)一個常量池,常量池就是該類型所用到常量的一個有序集合,包括直接常量(基本類型,String)和對其他類型. 字段和方法的符號引用

在這些概念中最容易搞錯的就是堆和棧的區(qū)分。當(dāng)定義一個變量,Java虛擬機就會在棧中為該變量分配內(nèi)存空間 這部分內(nèi)存空間會馬上被用作新的空間進(jìn)行分配,如果使用new的方式創(chuàng)建一個變量, 那么就會在堆中為這個對象分配內(nèi)存空間,即使該時象的作用域結(jié)束, 這部分內(nèi)存也不會立即被回收.而是等待系統(tǒng)Gc進(jìn)行回收,堆的大小隨著手機的不斷發(fā)展而不斷變大,在過程中.可以使用如下所示的代碼來獲得堆的大小,所謂的
內(nèi)存分析,正是分析Heap中的內(nèi)存狀態(tài)。

ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
int heapSize = manager.getLargeMemoryClass();

獲取Android系統(tǒng)內(nèi)存信息

內(nèi)存回收

java對于C/C+這類語言最大的優(yōu)勢就是不用手動管理系統(tǒng)資源,java創(chuàng)建了垃圾收集器線程來自動進(jìn)行資源的管理 這樣做的好處是大大降低了程序開發(fā)人員對內(nèi)存管理的繁瑣工作,但這也帶來了很多問題,例如java的gc是系統(tǒng)自動進(jìn)行的,但何時進(jìn)行卻是開發(fā)者無法控制的,即使調(diào)用system.gc方法,也只是建議系統(tǒng)進(jìn)行GC,但是系統(tǒng)是否采納你的建設(shè).那就不一定了。JVM虛擬機雖然能夠自動控制GC,但是再強大的算法,也難免會存在部分對象忘記回收的現(xiàn)象發(fā)生, 這就是造成內(nèi)存泄漏的原因

    1. Bitmap 優(yōu)化

Bitmap是造成內(nèi)存占用過高甚至是OOM的最大威脅,下面給出一些使用Bitmap的技巧

  • 使用適當(dāng)分辨率和大小的圖片
  • 及時回收內(nèi)存
  • 使用圖片緩存
  • 代碼優(yōu)化

關(guān)于代碼優(yōu)化:
對常量使用static修飾符
使用靜態(tài)方法,靜態(tài)方法會比普通方法提高15%左右的訪問速度
減少不必要的成員變量 ,這點在Android Lint工具上已經(jīng)集成檢測了.如果一個變量可以定義為局部變量,則會建議你不要定義為成成變量.
減少不必要的對象 使用基礎(chǔ)類型會比使用對象更加節(jié)省資源, 同時更應(yīng)該避免頻繁創(chuàng)建短作用域的變量
盡量不要使枚舉、少使用迭代器。
對Cursor,Receiver,Sensor,Fiie等對象,要非常注意對它們的創(chuàng)建.回收與注冊解注冊。
避免使用IOC框架.IOC通常使用注解.反射來進(jìn)行實現(xiàn),雖然現(xiàn)在java對反射的效率已經(jīng)進(jìn)行了很好的優(yōu)化.但大量使用反射依然會帶來性能的下降.
使用RenderScript,openGL來進(jìn)行非常復(fù)雜的繪圖操作.
使用surfaceView來替View進(jìn)行大量.頻繁的繪圖操作.
盡量使用視圖緩存,而不是每次都執(zhí)行inflaler()方法解析視圖

    1. Lint 工具

關(guān)于性能優(yōu)化工具的介紹,使用,找個時間再介紹吧,今天就先到這了

關(guān)于Android群英傳 這本書的筆記也就先寫到這, 后面再寫就會寫我在工作中實際遇到的問題,分析,以及解決的過程,權(quán)當(dāng)一個對自己成長的一個記錄;

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