天天看點

Android性能優化

Android裝置作為一種移動裝置,不管是記憶體還是CPU的性能都受到了一定的限制,無法做到像PC裝置那樣具有超大的記憶體和高性能的CPU,這也意味着Android程式不可能無限制地使用記憶體和CPU資源,過多地使用記憶體會導緻程式記憶體溢出,即OOM。而過多地使用CPU資源,一般指做大量的耗時任務,會導緻手機變得卡頓甚至出現無法響應的情況,即ANR。

1,布局優化

布局優化的思想很簡單,就是盡量減少布局檔案的層級,布局中的層級少了,這就意味着Android繪制時的工作量少了,那麼程式的性能自然就高了。

那麼如何進行布局優化呢?有以下兩點:

首先删除布局中無用的看控件和層級,其次有選擇地使用性能較低的ViewGroup,比如RelativeLayout。

可以采用标簽、标簽、ViewStub。标簽主要用于布局重用,标簽一般配合标簽使用,它可以降低減少布局的層級,而ViewStub則提供了按需加載的功能。

2,繪制優化

繪制優化是指View的onDraw方法要避免執行大量的操作,主要展現在兩個方面

onDraw中不要建立新的局部對象,這是因為onDraw方法可能會被頻繁調用,這樣就會在一瞬間産生大量的臨時對象,這不僅占用了過多的記憶體而且還會導緻系統會更頻繁gc,降低程式的執行效率。

onDraw方法中不要做耗時的任務,也不能執行成千上萬次的循環操作,盡管每次循環都很輕量級,但是大量的循環仍然十分搶占CPU的時間片,這會造成View的繪制過程很不流暢。

3,記憶體優化

記憶體洩露在開發過程中是一個需要重視的問題,記憶體優化分為兩個方面,一方面是在開發過程中避免寫出有記憶體洩露的代碼,另一方面是通過一些分析工具比如MAT來找出潛在的記憶體洩露繼而解決。

場景1:靜态變量導緻記憶體洩露

比如下面這段代碼:

MainActivity無法正常銷毀,因為靜态變量sContext引用了它。同樣,sView是一個靜态變量,他内部持有了目前Activity,是以Activity仍然無法釋放。

場景2:單例模式導緻記憶體洩露

靜态變量導緻的記憶體洩露都太過明顯了,但單例模式所帶來的記憶體洩露是我們容易忽視的。比如下面這段代碼:

首先提供一個單例模式的TestManager,TestManager可以接收外部的注冊并将外部的監聽器存儲起來。然後用Activity實作OnDataArrivedListener接口并向TestManager注冊監聽,但是如果缺少解注冊的操作,會引起記憶體洩露。比如下面這段代碼:

Activity的對象被單例模式的TestManager所持有,而單例模式的特點是其生命周期和Application保持一緻,是以Activity對象無法被及時釋放。

場景3:屬性動畫導緻的記憶體洩露

從Android3.0開始,Google提供了屬性動畫,屬性動畫中有這麼一類無限循環的動畫,如果在Activity中播放此類動畫且沒有在onDestroy中停止動畫,那麼動畫就會一直播放下去,盡管已經無法在界面上看到動畫效果,但這個時候Activity的View會被動畫持有,而View又持有了Activity,最終Activity無法釋放。

4,響應速度優化和ANR日志分析

響應速度優化的核心思想是避免在主線程中做耗時操作,但是有時候的确有很多耗時操作,怎麼辦呢?可以将這些耗時操作放線上程中去執行,即采用異步的方式執行耗時操作。響應速度過慢更多地展現在Activity的啟動速度上面,如果在主線程中做太多的事情,會導緻Activity啟動出現黑屏現象,甚至出現ANR。Android規定,Activity如果5秒鐘之内無法響應螢幕觸摸事件或者鍵盤輸入事件就會出現ANR,而BroadcastReceiver如果10秒之内還未執行完操作也會出現ANR,那麼在實際開發過程中遇到ANR,怎麼定位問題呢?其實當一個程序發生ANR了以後,系統會在/data/anr/目錄下建立一個檔案traces.txt,通過分析這個檔案就能定位出ANR的原因。比如下面代碼在Activity的onCreate中休眠30s,程式運作持續點選螢幕,應用一定會出現ANR:

5,ListView和Bitmap優化

ListView優化三個方面:

采用ViewHolder并避免在getView中執行耗時操作

根據清單的滑動狀态來控制任務的執行頻率,比如當清單快速滑動時顯然是不太适合開啟大量異步任務的。

嘗試開啟硬體加速來使ListView的滑動更加流暢。

Bitmap優化,主要是通過BitmapFactory.Options來根據需要對圖檔進行采樣,采樣過程中主要用到了BitmapFactory.Option的inSampleSize參數。

6,線程優化

線程優化的思想是采用線程池,避免在程式中存在大量的Thread。線程池可以重用内部的線程,進而避免了現場的建立和銷毀所帶來的性能開銷,同時線程池還能有效地控制線程池的最大并發數,避免大量的線程因互相搶占系統資源進而導緻阻塞現象發生。

7,其他性能優化建議

還有一些其他性能優化的小建議,通過它們可以在一定程度上提高性能:

避免建立過多的對象

不要過多使用枚舉,枚舉占用的記憶體空間要比整型大

常量請用static final來修飾

使用一些Android特有的資料結構,比如SpareArray和Pair等,它們都具有更好的性能

适當使用軟引用和弱引用

采用記憶體緩存和磁盤緩存

盡量采用靜态内部類,這樣可以避免在的由于内部類而導緻的記憶體洩露

MAT的全稱是Eclipse Memory Analyzer,他是一款強大的記憶體洩露分析工具。MAT提供了很多功能,但是最常用的隻有Histogram和Dominator Tree,通過Histogram可以直覺看出記憶體中不同類型的buffer的數量和占用的記憶體大小,而Dominator Tree則把記憶體中的對象按照從大到小的順序進行排序,并且可以分析對象之間的引用關系,記憶體洩露分析就是通過Dominator Tree來完成。在Dominator Tree中記憶體洩露的原因一般不會直接顯示出來,這個時候需要按照從大到小的順序去排查一遍。

主要是提高代碼的可維護性和可擴充性,而程式的可維護性本質上也包含可擴充性。

命名要規範,要能正确地傳達出變量或者方法的含義,少用縮寫,關于變量的字首可以參考Android源碼的命名方式,比如私有方式以m開頭,靜态成員以s開頭,常量則全部用大寫字母表示,等等。

代碼的排版上需要留出合理的空白區分不同的代碼塊,其中同類變量的聲明要放在一組,兩類變量之間要留出一行空白作為區分。

僅為非常關鍵的代碼添加注釋,其他地方不寫注釋,這就對變量和方法的命名風格提出了很高的要求。

代碼的層次性指代碼要有分層的概念,對于一段業務邏輯,不要試圖在一個方法或者一個類中去全部實作,它可以分成幾個子邏輯,然後每個子邏輯做自己的事情。單一職責是和層次性相關聯,代碼分成以後,每一層僅僅關注少量的邏輯,這樣就做到了單一職責。

源于對掌握的Android開發基礎點進行整理,羅列下已經總結的文章,從中可以看到技術積累的過程。

1,Android系統簡介

2,ProGuard代碼混淆

3,講講Handler+Looper+MessageQueue關系

4,Android圖檔加載庫了解

5,談談Android運作時權限了解

6,EventBus初了解

7,Android 常見工具類

8,對于Fragment的一些了解

9,Android 四大元件之 " Activity "

10,Android 四大元件之" Service "

11,Android 四大元件之“ BroadcastReceiver "

12,Android 四大元件之" ContentProvider "

13,講講 Android 事件攔截機制

14,Android 動畫的了解

15,Android 生命周期和啟動模式

16,Android IPC 機制

17,View 的事件體系

18,View 的工作原理

19,了解 Window 和 WindowManager

20,Activity 啟動過程分析

21,Service 啟動過程分析

22,Android 性能優化

23,Android 消息機制

24,Android Bitmap相關

25,Android 線程和線程池

26,Android 中的 Drawable 和動畫

27,RecylerView 中的裝飾者模式

28,Android 觸摸事件機制

29,Android 事件機制應用

30,Cordova 架構的一些了解

31,有關 Android 插件化思考

32,開發人員必備技能——單元測試