App性能優化淺談

前言

前段時間給公司的小夥伴們進行了關於app性能優化的技術分享。這裏我稍微整理一下也給你們分享一下。關於性能優化這個話題很是大,涉及面可以很是廣,也可以很是深刻。本人能力有限,不會給你們講特別難懂,特別底層的東西。都是咱們開發能着手去作的點。你們都在講性能優化,但對於項目經驗不夠豐富的朋友很是難有一個概念。作優化的時候也會比較茫然,這裏我就給你們指明方向。數據庫

從何講起?

筆者在作產品開發的時候,也遇到性能瓶頸。測試project師反饋了一些比較明顯的問題,比方UI界面的過分繪製,列表滑動有明顯卡頓,比較耗內存等等,但以往的都沒有針對性的去作對應的優化,因此藉着保證產品質量的出發點,本身定了相關的性能優化方案,可能不太成熟。只是可以逐步無缺。並找到最適合本身產品的優化方案。緩存

這裏我定了四個方向:
- 響應時間(Response Time)
- 界面卡頓(ANR)
- 耗內存(Memory)
- 內存泄露(Out of memory)安全

響應時間

這裏指的是client與服務端交互,拿到數據、解析、再到顯示到界面整個過程耗費的時間。性能優化

這個部分涉及client的優化。也涉及服務端的優化,這裏僅僅討論client。markdown

HTTP請求方式

咱們的app通常離不開網絡,請求接口是最尋常的操做了,怎樣請求,請求什麼咱們在開發初期就要定好,服務端給個人提供的接口,大體可以經過GET、POST、HEAD、PUT、DELETE這幾種請求方式,不一樣的請求方式有不一樣應用場景,比方GET請求。應當用來請求返回結果。參數是做爲url的一部分;POST請求。用於請求會更改服務端數據或狀態。HEAD請求跟GET同樣。僅僅是server不能在響應裏返回消息主體;PUT請求,用於將網頁放置正確的地方;DELETE請求用於刪除server指定文檔。網絡

使用優秀的開源Http框架是咱們比較好的選擇。它的優勢是通過市場的驗證,很是多坑都被填過,缺點也是咱們需要去深究它才幹對其進行擴展。遇到坑也不必定能填。app

假設本身造輪子的話。還需要咱們花時間去驗證去適應咱們的業務需求,但優勢是咱們可以本身去擴展可把控,只是這很是考量開發人員的素養。框架

數據解析

實際開發其中服務端的返回數據格式無非就兩種:
- JSON
- XMLdom

這兩種格式數據格式各有優劣。從可讀性來看,xml稍微好一點,只是JSON也有規範的標籤,從解析難度和速度來看,你們都比較傾向使用JSON,眼下JSON也是主流的數據格式。異步

在Android中均可以使用優秀的解析庫來加快咱們的解析速度,XML中有dom4j,JSON有Jackson、Gson。咱們經過這些庫實現咱們更快的完畢數據解析,提升咱們的開發效率。

數據存儲

上一節講的是數據解析。咱們解析完後的數據,可能就需要將數據存儲在某個地方,Android的五種存儲方式:
- Content Provider(主要用來向其它應用程序共享數據)
- SQLite(存儲數據到數據庫中)
- File(本地文件保存)
- SharedPreference(主要用來保存簡單的配置信息)
- 網絡存儲(WebService返回的數據或是解析HTTP協議實現網絡數據交互)

爲了提升應用程序的響應時間,數據緩存是一個比較好的方式,咱們可以預處理server返回的數據,對數據進行緩存刷新。

優化點:
- 異步請求網絡數據
- 預處理server返回數據
- 異步進行數據存儲操做
- 數據緩存刷新
- Timeout超時重試
- 在主線程中操做UI

界面卡頓

ANR表示」應用程序無響應」,這個是需要咱們避免發生的事情,出現這個異常的緣由:
- 主線程 (「事件處理線程」 / 「UI線程」) 在5秒內沒有響應輸入事件
- BroadcastReceiver在10秒內沒有運行完畢

致使ANR的緣由有很是多,普通狀況就是在UI線程作了耗時的操做,好比」網絡請求」、數據庫操做。

那麼怎樣避免?
- UI線程僅僅作界面刷新,不作不論什麼耗時操做。耗時操做放在子線程來作
- 可以使用Thread+handle或者AsyncTask來進行邏輯處理

耗內存

每部手機的內存有限,咱們這裏所說的內存指的是手機的RAM,它是Ramdom Access Memory的縮寫。咱們應用程序的需要隨機讀寫的數據就存在RAM中,Android手機之因此會比較耗內存。這跟Android後臺的處理有關,咱們知道Android應用是使用Java開發的。運行Java需要有虛擬機,說明每開啓一個應用都會建立一個虛擬機。而這是需要內存的,因此咱們開的應用越多,後臺進程越多。內存都分配出去了,才致使內存消耗的嚴重。

事實上這個問題咱們是沒得破的,僅僅要內存不夠。咱們的應用仍是會卡。咱們開發的應用依賴與系統給咱們分配的堆內存,通常上限在16M~48M,但咱們可以經過在AndroidManifest設置Application屬性largeHeap=「true」來申請不少其它的堆內存。

經過下面代碼獲取可用堆內存限制:

mActivityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
 mMaxMemory = mActivityManager.getMemoryClass();

內存泄露

內存泄露這個問題已經被說爛了。你們都知道有內存泄露這個問題存在,但爲何會發生內存泄露?

這裏的內存泄露並不是真正意思上的泄露。而是因爲內存不足不能進行GC操做,從而致使佔用內存過大。拋出out of memory異常,而被系統Kill掉。

JVM回收機制

是時候講講JVM的回收機制了。看下圖:

JVM分代

JVM對Java對象分了三個代進行管理。分別爲年輕代、年老代、永久代。
年輕代(Young Generation):絕大多數的Java對象會在年輕代被分配,也會在年輕代被回收。
年老代(Old Generation):在年輕代長期存在沒有被回收的Java對象會轉移到年老代,這個堆空間通常會被比年輕代的堆空間要大。


永久代:存放VM和Java類的元數據。以及interned字符串和類的靜態變量。

這裏涉及到JVM的相關知識,這裏不繼續深刻探討。

但咱們應該可以知道垃圾回收器的做用:
- 分配內存
- 保證所有正在被引用的對象還存在於內存中
- 回收運行代碼已經再也不引用的對象所佔的內存

對象引用

Java的引用類型可以分爲下面幾種:
- 強引用(Strong Ref):強可達,去掉強可達,纔會被回收。


- 軟引用(Soft Ref):內存夠用。就保持,內存吃緊。則回收,主要用來作緩存。
- 弱引用(Weak Ref):比Soft Ref弱,即便內存不吃緊也會被回收。


- 虛引用(Phantom Ref):不會在內存保持不論什麼對象。

一圖勝千言:

對象引用

利用Strong Ref,存儲大量數據。直到heap撐破,利用inter strings(或者class loader載入大量的類)把perm gen撐破,而後就是內存泄露了。

怎樣優化?

前面講了一些背景知識,對咱們理解內存優化有必定的幫助,下面就簡單說一下咱們優化的方向:
- 佈局優化
- 內存優化

佈局優化

你們可以拿出大家的Android機
開發人員工具-Profile GPU Rendering-選擇在屏幕上顯示條形圖

-藍色表明測量繪製Display List的時間
-紅色表明OpenGL渲染Display List所需要的時間
-黃色表明CPU等待GPU處理的時間
-中間綠色橫線表明VSYNC時間16ms。儘可能將所有條形圖控制在這條綠線下

爲何是16ms?

Android 通知界面渲染和重繪的時間要在16ms內完畢。假設超過16ms,就會致使丟幀,也就是咱們常說的卡頓。

優化點:
- 避免OverDraw
- 優化佈局層級
- 避免過多無用嵌套
- 使用<include>標籤重用layout
- 使用<ViewStub>延遲載入
- Hierarchy View進行層級分析

詳細的用法,這裏不介紹了,不懂就百度。

內存優化

內存優化的點有很是多,這裏我主要分爲兩大塊:
- Bitmap優化
- 代碼優化

Bitmap優化

  1. 使用適當分辨率和大小的圖片
  2. 及時回收內存(bitmap.recycle())
  3. 使用圖片緩存(LruCache和DiskLruCache)

第一點,就是按需顯示。比方列表中的圖片,你可以顯示縮略圖,詳情頁,你就可以載入對應的分辨率的圖片。這樣可以下降內存消耗,通常可以要求服務端提供多種分辨率的圖片。

第二點。Bitmap是很是耗內存,尤爲是載入比較大的bitmap,可以想到的優化方案就是使用記得回收,對Bitmap進行壓縮,使用BitmapFactory.Options設置inSampleSize就可以縮小圖片。

第三點。圖像緩存,這個可以利用成熟的圖片載入框架,比方Universal-ImageLoader、Fresco、Picasso,這些框架都對圖片進行了很是好的優化,你們可以對照一下,選擇使用就能夠。

代碼優化

關於代碼這個就有的說了,不論什麼能改進咱們程序的優化點都能寫在這裏,這裏沒辦法把所有優化的點列在這裏,僅僅提供相關的參考,剩下的就好各位經驗總結和積累了。

優化點:
- 對常量使用static修飾符
- 使用靜態方法
- 下降沒必要要的成員變量
- 儘可能不要使用枚舉。少用迭代器
- 對Cursor、Receiver、Sensor、File等對象。要注意它們的建立、回收與註冊、反註冊
- 避免大量使用註解、反射
- 使用RenderScript、OpenGL來進行復雜的畫圖操做
- 使用SurfaceView來替代View進行大量、頻繁的畫圖操做
- 儘可能使用視圖緩存,而不是每次都運行inflate()方法解析視圖

注:這裏引用了Android羣英傳的相關優化點

  • 建立新的對象都需要額外的內存空間。要儘可能下降建立新的對象。
  • 將類、變量、方法等等的可見性改動爲最小。
  • 針對字符串的拼接,使用StringBuffer替代String。
  • 不要在循環其中聲明暫時變量,不要在循環中捕獲異常。

  • 假設對於線程安全沒有要求,儘可能使用線程不安全的集合對象。
  • 使用集合對象,假設事先知道其大小。則可以在構造方法中設置初始大小。

  • 文件讀取操做需要使用緩存類。及時關閉文件。
  • 慎用異常,使用異常會致使性能下降。
  • 假設程序會頻繁建立線程,則可以考慮使用線程池。

以上都是些經驗總結,大體都相差無幾,朋友們在作代碼優化的時候。可以依據這些優化點,有針對性去重構代碼,事實上最重要仍是代碼的可讀性,結構清晰。

性能優化工具

  • Memory Monitor - 內存監視工具
  • TraceView
  • MAT

Android開發人員對與以上幾個性能調優的工具必定不陌生。這裏我也再也不寫那麼多廢話了,關於它們的用法,官網另外一些大牛的博客都有介紹。

最後

寫這篇文章的出發點也是對Android性能優化有個比較清晰的認識,不論什麼事情都不可能一蹴而就,需要循循漸進,對一個剛開始學習的人你談優化很是不現實。咱們先把主要的作好,再去考慮對應的優化,筆者也在不斷學習其中。借鑑別人好的優化方案,提升產品的質量。感謝你們對筆者的關注。

相關文章
相關標籤/搜索