Android 性能優(yōu)化(一)

Android作為一種移動設備,內(nèi)存和CPU資源都是受限的. 過多地使用內(nèi)存會導致內(nèi)存溢出(OOM),過多的使用CPU會導致手機卡頓,甚至出現(xiàn)程序無法響應的情況(ANR). 本文介紹一系列優(yōu)化方法.

1. 布局優(yōu)化

1.1 盡量減少布局的層級.

布局的層級少了,那么Android繪制時的工作量就少了,自然能后提高性能.

1.2 ViewGroup的選擇.

有選擇的使用性能較低的ViewGroup如RelativeLayout的功能比較復雜繪制過程消耗更多的CPU資源 , 盡量使用性能較高的ViewGroup如LinearLayout 和FrameLayout.

1.3 <include> 標簽

<include> 標簽可以將指定的布局加載到當前布局 .

定義一個布局文件,layout_include.xml文件.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:background="#f00"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <Button
        android:layout_width="wrap_content"
        android:text="測試按鈕"
        android:textSize="30sp"
        android:layout_height="wrap_content"/>
</LinearLayout>

使用 <include>標簽引用布局.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/activity_main"
    android:background="#0f0"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- 使用 include 標簽 -->
    <include
        android:layout_height="200dp"
        android:layout_width="200dp"
        layout="@layout/layout_include"/>
</LinearLayout>

效果圖如下:

效果圖

1.4 <merge> 標簽

<merge> 標簽主要和 <include> 標簽一起使用,比如在上面的樣例中當前布局使用了一個豎直方向的LinearLayout,在被引用的布局中也使用了一個豎直方向的LinearLayout此時內(nèi)層的LinearLayout就顯得多余了.可以使用<merge>標簽替代.從而達到減少布局層級的目的.

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <Button
        android:layout_width="wrap_content"
        android:text="測試按鈕"
        android:textSize="30sp"
        android:layout_height="wrap_content"/>
</merge>

1.5 ViewStub

ViewStub繼承自View并且寬高都是 0,它本身不參與任何布局和繪制過程. 他可以按需添加布局,有些布局一開始是不需要的,因此可以先不加載,在需要時再進行布局加載.

加載ViewStub前界面

加載ViewStub后界面

創(chuàng)建layout_viewstub.xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/stub_import"
              xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">
    <TextView
        android:background="#00f"
        android:layout_width="match_parent"
        android:text="View_Stub測試界面"
        android:textSize="30sp"
        android:layout_height="match_parent"/>
</LinearLayout>

activity_main.xml 中添加ViewStub節(jié)點

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/activity_main"
    android:background="#0f0"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 使用 include 標簽 -->
    <include
        android:layout_height="200dp"
        android:layout_width="200dp"
        layout="@layout/layout_include"/>
    <!-- 使用ViewStub -->
    <ViewStub
        android:layout_width="200dp"
        android:id="@+id/panel_import"
        android:layout="@layout/layout_viewstub"
        android:inflatedId="@+id/stub_import"
        android:layout_height="200dp"/>
    <Button
        android:layout_width="wrap_content"
        android:onClick="onclick"
        android:text="加載布局"
        android:layout_height="wrap_content"/>
</LinearLayout>

修改MainActivity.java

public void onclick(View view) {
    // 加載ViewStub方式一
    // ((ViewStub)findViewById(R.id.panel_import)).setVisibility(View.VISIBLE);
    // 加載ViewStub方式二  
    ((ViewStub)findViewById(R.id.panel_import)).inflate();
}

android:id="@+id/panel_import" : 設置ViewStub的id.
android:id="@+id/panel_import" : ViewStub加載的布局的根布局的id.
android:layout="@layout/layout_viewstub" : 設置加載布局的資源id.
注意 : ViewStub不支持<merge>標簽

2. 繪制優(yōu)化

繪制優(yōu)化主要是指View的onDraw方法要避免大量操作. 主要體現(xiàn)如下兩方面 :

  • onDraw中不要創(chuàng)建新的局部對象, 這是應為onDraw方法會被頻繁調(diào)用,這樣會在一瞬間產(chǎn)生大量臨時對象.這樣會造成過多的內(nèi)存效果,更多的GC,降低程序的執(zhí)行效率.
  • onDraw方法中不要進行耗時操作,也不能執(zhí)行大量的循環(huán)操作.雖然每次都是輕量級的但是大量循環(huán)還是會十分搶占CPU的時間片.造成View的繪制不流暢.按照Google給出的性能優(yōu)化典范中的標準,View的幀率保證60fps是最好的.這就要求每一幀圖像的繪制不要超過 1000 / 60 = 16ms .因此盡量減低onDraw的復雜度有利于提高性能.

3. 內(nèi)存泄露優(yōu)化

內(nèi)存泄露的優(yōu)化分為兩部分.

  • 開發(fā)過程中避免寫內(nèi)存泄露的代碼.
  • 通過一些工具如MAT找出潛在的內(nèi)存泄露,然后解決掉.

3.1 靜態(tài)變量導致內(nèi)存泄露

public class MainActivity extends AppCompatActivity {
    private static Context sContext;
    private static View sView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 設置
        sContext = this;
        sView = new View(this);
    }
}

上面的代碼容易造成內(nèi)存泄露.

3.2 單例模式導致的內(nèi)存泄露

單例模式造成內(nèi)存泄露主要是單例對象保存的外部變量的引用沒有被移除造成的.比如單例對象有添加監(jiān)聽者的方法但是沒有移除監(jiān)聽者對象的方法.

3.3 屬性動畫導致的內(nèi)存泄露

Android 3.0 增加了屬性動畫,這種動畫需要在onDestory中取消動畫animator.cancel()否則動畫就會無限循環(huán)地執(zhí)行,造成動畫對象會持有View,View持有Activity,最終造成Activity無法釋放.造成內(nèi)存泄露.

屬性動畫持有關系

3.4 響應速度優(yōu)化和ANR日志分析

Android系統(tǒng)規(guī)定:

  • Activity如果 5 秒內(nèi)無法響應屏幕觸摸或者鍵盤輸入時間就會出現(xiàn)ANR.
  • BroadcastReciever 10秒內(nèi)沒有執(zhí)行完操作,就會出現(xiàn)ANR.

當系統(tǒng)發(fā)生ANR時會在 data/anr目錄下生成traces.txt文件.通過分析這個文件就可以找到ANR的原因.

我們在onCreate方法中添加如下代碼模模擬ANR

while(true);

查看traces.txt部分內(nèi)容如下:

traces.txt 文件部分內(nèi)容

便可以定位出問題所在了.

4. ListView 和 Bitmap優(yōu)化.

4.1 ListView 優(yōu)化主要有三個方面

  • 使用ViewHolder,避免在getView中進行耗時操作.
  • 根據(jù)列表的滑動狀態(tài)來控制任務的執(zhí)行頻率,比如當列表快速滑動時不適合開啟大量異步任務.
  • 開啟硬件加速來使ListView更加流暢.

4.2 Bitmap優(yōu)化

Bitmap優(yōu)化主要是通過加載壓縮后的圖片來進行優(yōu)化的.

5. 線程優(yōu)化

盡量使用線程池代替開啟新的線程.

6. 性能優(yōu)化建議

  • 避免創(chuàng)建過多的對象.
  • 不要過多的使用枚舉,枚舉占用的空間比整形大.
  • 常量使用 static final 來修飾.
  • 使用一些Android 特有的數(shù)據(jù)結構,SparseArray 和 Pair 他們具有更好的性能.
  • 適當使用軟引用和弱引用.
  • 采用內(nèi)存緩存和磁盤緩存.
  • 盡量使用靜態(tài)內(nèi)部類,避免潛在的由于內(nèi)部類導致的內(nèi)存泄露.

參考 : <Android開發(fā)藝術探索>

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市撑毛,隨后出現(xiàn)的幾起案子慎王,更是在濱河造成了極大的恐慌瞎惫,老刑警劉巖虏两,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掸哑,死亡現(xiàn)場離奇詭異厢拭,居然都是意外死亡它匕,警方通過查閱死者的電腦和手機省骂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門蟀淮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人钞澳,你說我怎么就攤上這事怠惶。” “怎么了轧粟?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵策治,是天一觀的道長。 經(jīng)常有香客問我兰吟,道長通惫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任混蔼,我火速辦了婚禮履腋,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己遵湖,他們只是感情好悔政,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著延旧,像睡著了一般谋国。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上迁沫,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天芦瘾,我揣著相機與錄音,去河邊找鬼集畅。 笑死近弟,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的挺智。 我是一名探鬼主播藐吮,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逃贝!你這毒婦竟也來了谣辞?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤沐扳,失蹤者是張志新(化名)和其女友劉穎泥从,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沪摄,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡躯嫉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了杨拐。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祈餐。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖哄陶,靈堂內(nèi)的尸體忽然破棺而出帆阳,到底是詐尸還是另有隱情,我是刑警寧澤屋吨,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布蜒谤,位于F島的核電站,受9級特大地震影響至扰,放射性物質發(fā)生泄漏鳍徽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一敢课、第九天 我趴在偏房一處隱蔽的房頂上張望阶祭。 院中可真熱鬧绷杜,春花似錦、人聲如沸濒募。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萨咳。三九已至,卻和暖如春疫稿,著一層夾襖步出監(jiān)牢的瞬間培他,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工遗座, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舀凛,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓途蒋,卻偏偏與公主長得像猛遍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子号坡,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容