Android性能優化

Android性能優化

  • 爲什麼要進行性能優化?
  • 有哪些可以進行性能優化?

爲什麼要進行性能優化?

隨着項目版本的不斷迭代,App的性能問題會逐漸的暴露出來,給用戶帶來一些卡頓、崩潰的體驗。面對給和用戶造成的不良效果,做出了性能優化,提升App整體性能,帶用戶帶來良好的用戶觸感。

有哪些可以進行性能優化?

  • 內存優化
  • UI優化
  • 網絡優化
  • 啓動優化
  • 電量優化

1.內存優化

內存泄漏是Android的常客。那麼什麼是內存泄漏呢?內存不在GC的掌控範圍之內了。那麼java的GC內存回收機制是什麼?某對象不在有任何引用的時候纔會進行回收。那麼GC回收機制的原理是什麼?我們先來看張圖。

在這裏插入圖片描述

當我們向上尋找,一直尋找到GC Root的時候,此對象不會進行回收,例如,一個Activity。那麼如果我們向上尋找,直到找到GC Root對象的時候,就說明它是不可以回收的,

例如,我定義了一個int a;但是這個數據,我整個頁面或者說整個項目都沒有用到,則這個對象會被GC掉。

GC的引用點

  • java棧中引用的對象
  • 方法靜態引用的對象
  • 方法常量引用的對象
  • Native中JNI引用的對象
  • Thread——“活着的”線程

如何判斷

那麼我們如何判斷一個對象是一個垃圾對象,可以講他進行回收呢?

瞭解如何GC

舉了小例子教你們如何區分:

一般在學校吃飯,我們有兩種情況,
第一:吃完飯就直接走人,碗筷留給阿姨來收拾處理。
第二:吃完之後把碗筷放到收盤處直接進行回收。
但我們是個有素質的人,一般採用第二種情況,但根據想法,我們更傾向於第一種。
那麼一般在飯店或者KFC中,都是第一種情況。
那麼此時,問題來了,如果我已經吃完飯,然後我並沒有離開飯店,做在位置上和朋友吹吹牛逼,談談理想,聊聊人生。
那麼桌上那一堆碗筷是收還是不收?講道理是不能收的。雖然實際也是不能收的。因爲顧客是上帝~~~

So,我們如何判斷一個對象是一個可回收的垃圾對象呢?這是我們的一個主觀的判斷。但是有種情況我們是必須要考慮到的,沒錯,就是內存過多無法釋放的時候,會直接導致OOM。整個項目boom炸了。什麼鬼?outofmemory。沒錯就是它。

內存溢出

分析原因

我們需要分析內存溢出的原因,我們先來看一張圖:

在這裏插入圖片描述
內存泄漏一般導致應用卡頓,極端情況會導致項目boom。Boom的原因是因爲超過內存的閾值。原因主要有兩方面:

  • 代碼存在泄漏,內存無法及時釋放導致oom(這個我們後面說)
  • 一些邏輯消耗了大量內存,無法及時釋放或者超過導致oom

所謂消耗大量的內存的,絕大多數是因爲圖片加載。這是我們oom出現最頻繁的地方。我有寫過圖片加載的方法,一個是控制每次加載的數量,第二,保證每次滑動的時候不進行加載,滑動完進行加載。一般情況使用先進後出,而不是先進先出。不過一般我們圖片加載都是使用fresco或者Glide等開源庫。我們來看下下面兩張圖:

在這裏插入圖片描述
在這裏插入圖片描述
對比兩張圖,我們可以在第一張的情況出現了oom情況,我們通過log打印發現,處理的好像沒什麼問題,換句話說,如果我不放那0.8M的圖片。然後繼續不停的操作同樣會出現OOM,然而我們就蒙了。沒什麼圖片加載怎麼就這麼崩掉了。

內存分析工具

  • Heap SnapShot工具
  • Heap Viewer工具
  • LeakCanary工具(常用)
  • MAT工具

注意事項

  • 我們儘量不要使用Activity的上下文,而是使用application的上下文,因爲application的生命週期長,進程退出時纔會被銷燬。所以,單例模式是最容易造成內存溢出的原本所在,因爲單例模式的生命週期的應該和application的生命週期一樣長,而不是和Activity的相同。
  • Animation也會導致內存溢出,爲什麼?因爲我們是通過view來進行演示的,導致view被Activity持有,而Activity又持有view。最後因爲Activity無法釋放,導致內存泄漏。解決方法是在Activity的ondestory()方法中調用Animation.cancle()進行停止,當然一些簡單的動畫我們可以通過自定義view來解決。至少我現在已經很少使用Animation了。沒有一個動畫是自定義view解決不了的。如何有,那就是兩個~~~。

2.UI優化

UI優化主要包括佈局優化以及view的繪製優化。不急,我們接下來一個一個慢慢看~~。先說下UI的優化到底是什麼?有些時候我們打開某個軟件,會出現卡頓的情況。這就是UI的問題。那麼我們想一下,什麼情況會導致卡頓呢?一般是如下幾種情況:

  • 人爲在UI線程中做輕微耗時操作,導致UI線程卡頓;
  • 佈局Layout過於複雜,無法在16ms內完成渲染;
  • 同一時間動畫執行的次數過多,導致CPU或GPU負載過重;
  • View過度繪製,導致某些像素在同一幀時間內被繪製多次,從而使CPU或GPU負載過重;
  • View頻繁的觸發measure、layout,導致measure、layout累計耗時過多及整個View頻繁的重新渲染;內存頻繁觸發GC過多(同一幀中頻繁創建內存),導致暫時阻塞渲染操作;
  • 冗餘資源及邏輯等導致加載和執行緩慢;
  • 臭名昭著的ANR;

可以看見,上面這些導致卡頓的原因都是我們平時開發中非常常見的。有些人可能會覺得自己的應用用着還蠻OK的,其實那是因爲你沒進行一些瞬時測試和壓力測試,一旦在這種環境下運行你的App你就會發現很多性能問題。

佈局優化

GPU繪製
我們對於UI性能的優化還可以通過開發者選項中的GPU過度繪製工具來進行分析。在設置->開發者選項->調試GPU過度繪製(不同設備可能位置或者叫法不同)中打開調試後可以看見如下圖(對settings當前界面過度繪製進行分析):

在這裏插入圖片描述
在這裏插入圖片描述
可以發現,開啓後在我們想要調試的應用界面中可以看到各種顏色的區域,具體含義如下:

在這裏插入圖片描述
Overdraw有時候是因爲你的UI佈局存在大量重疊的部分,還有的時候是因爲非必須的重疊背景。例如某個Activity有一個背景,然後裏面的Layout又有自己的背景,同時子View又分別有自己的背景。僅僅是通過移除非必須的背景圖片,這就能夠減少大量的紅色Overdraw區域,增加藍色區域的佔比。這一措施能夠顯著提升程序性能。

如果佈局中既能採用RealtiveLayout和LinearLayout,那麼直接使用LinearLayout,因爲Relativelayout的佈局比較複雜,繪製的時候需要花費更多的CPU時間。如果需要多個LinearLayout或者Framelayout嵌套,那麼可採用Relativelayout。因爲多層嵌套導致佈局的繪製有大部分是重複的,這會減少程序的性能。

代碼優化

Android Studio自帶的代碼檢查工具。打開Analyze->Run Inspection by Name… –>unused resource 點擊開始檢測,等待一下後會發現如下結果:
在這裏插入圖片描述
我們還可以這樣,將鼠標放在代碼區點擊右鍵->Analyze->Inspect Code–>界面選擇你要檢測的模塊->點擊確認開始檢測,等待一下後會發現如下結果:
在這裏插入圖片描述
上面那兩種方法是最容易找到代碼缺陷以及無用代碼的地方。所以盡情的入坑去填坑把~~~

繪製優化

那麼什麼是繪製優化?繪製優化主要是指View的onDraw方法需要避免執行大量的操作。我將分爲了2個方面。

  • onDraw方法不需要創建新的局部對象,這是因爲onDraw方法是實時執行的,這樣會產品大量的臨時對象,導致佔用了更多內存,並且使系統不斷的GC。降低了執行效率。
  • onDraw方法不需要執行耗時操作,在onDraw方法裏少使用循環,因爲循環會佔用CPU的時間。導致繪製不流暢,卡頓等等。Google官方指出,view的繪製幀率穩定在60dps,這要求每幀的繪製時間不超過16ms(1000/60)。雖然很難保證,但我們需要儘可能的降低。

3.網絡優化

線程是我們項目中不可缺少的重要部分,因爲我們大多數數據都是從網絡獲取的。線程這個是必備用品。我們依舊可以通過Memory下面的Net進行網絡的監聽:
在這裏插入圖片描述

ANR問題

那麼什麼是ANR?

Application Not Responding。應用程序無響應。

那麼一般什麼時候會出現ANR?

Android官方規定:

  • activity如果5s內無響應事件(屏幕觸摸事件或者鍵盤輸入事件)。
  • BroadcastReceiver如果在10s內無法處理完成。
  • Service如果20s內無法處理完成。這三種情況會導致ANR。

用張簡潔的圖來介紹把。看起來方便~~
在這裏插入圖片描述
線程優化

上面說的三種導致ANR的情況,絕大多數就是因爲線程阻塞導致的。那麼我們應該如何處理呢?

Android系統爲我們提供了若干組工具類來解決此問題。

  • Asynctask:爲UI線程與工作線程之間進行快速處理的切換提供一種簡單便捷的機制。適用於當下立即需要啓動,但是異步執行的生命週期短暫的場景。
  • HandlerThread:爲某些回調方法或者等待某些執行任務的執行設置一個專屬的線程,並提供線程任務的調度機制。
  • ThreadPool:把任務分解成不同的單元,分發到各個不同的線程上,進行同時併發處理。
  • IntentService:適合執行由Ui觸發的後臺任務。並可以把這些任務執行的情況通過一定的機制反饋給UI。

網絡請求耗時會給用戶帶來卡頓的產品體驗,雖然可以使用Loading提升用戶體驗,但屬於治標不治本。例如,當網絡差的時候我們公司的項目一個loading就是10多s。甚至更多…

一般多線程的情況我們可以通過Asynctask處理。我前面有說過annotation(或者可以使用Rxjava)。這是google官方推出的註解。比bufferknife強大很多。這個可以快捷方便的處理多線程而且不會導致線程阻塞,而且你也可以控制線程的順序,例如我要執行完線程A後,根據線程A的某個參數來執行線程B。以此類推…

至於線程池麼,最多的還是要說道圖片加載了~~。圖片加載用三方就行了~想看詳細介紹,我前面有說,當然除了這個還有下載操作。這就和IntentService有關聯了。

圖片處理

  • 使用WebP格式;同樣的照片,採用WebP格式可大幅節省流量,相對於JPG格式的圖片,流量能節省將近 25% 到 35 %;相對於 PNG 格式的圖片,流量可以節省將近80%。最重要的是使用WebP之後圖片質量也沒有改變。後臺的小夥伴們看看是不是要處理下?

  • 使用縮略圖,我在前面寫圖片加載有說過,就是控制他的inside和option。然後進行圖片縮放。壓縮?講道理…我並不知道網絡圖片怎麼壓縮,but,我會縮放啊~~反正也不會失真。啦啦啦~咬我啊?

網絡請求處理

我們可以對服務端返回數據進行緩存,設定有效時間,有效時間之內不走網絡請求,減少流量消耗。對網絡的緩存可以參見圖片的三級緩存

在某些情況,我們儘量少使用GPS定位,如果條件允許,儘可能使用網絡定位。

下載、上傳,我們儘可能使用斷點續傳,斷點下載也是我們的必修課~

刷新數據時,儘可能使用局部刷新,而不是全局刷新,

第一、界面會閃屏一下,網差的界面直接白屏一段時間也不是不可能。
第二、流量的使用!!一個閃屏的緩存60+M,我清個3、5次緩存,在打開個3、5次。好了,2分鐘時間,我一個月流量就沒了。。。我前面提到的網絡緩存很重要。

4.啓動優化

安卓應用的啓動方式分爲三種:冷啓動、暖啓動、熱啓動,不同的啓動方式決定了應用UI對用戶可見所需要花費的時間長短。顧名思義,冷啓動消耗的時間最長。基於冷啓動方式的優化工作也是最考驗產品用戶體驗的地方。談及優化之前,我們先看看這三種啓動方式的應用場景,以及啓動過程中系統都做了些什麼工作。

冷啓動

爲什麼說冷啓動是耗時最長的。冷啓動是在啓動應用前,系統沒有獲取到當前app的activity、Service等等。例如,第一次啓動app。又或者說殺死進程後第一次啓動。那麼對比其他兩種方式。冷啓動自然是耗時最久的。

應用發生冷啓動時,系統一定會執行下面的三個任務:

  • 開始加載並啓動應用
  • 應用啓動後,顯示一個空白的啓動窗口(啓動閃屏頁)
  • 創建應用信息

那麼創建應用信息,系統就需要做一屁股的事:

  • application的初始化
  • 啓動UI線程
  • 創建Activity
  • 導入視圖(inflate view)
  • 計算視圖大小(onmesure view)
  • 得到視圖排版(onlayout view)
  • 繪製視圖(ondraw view)

這其中有兩個 creation 工作,分別爲 Application 和 Activity creation。他們均在 View 繪製展示之前。所以,在應用自定義的 Application 類和 第一個 Activity 類中,onCreate() 方法做的事情越多,冷啓動消耗的時間越長。

暖啓動

當應用中的 Activities 被銷燬,但在內存中常駐時,應用的啓動方式就會變爲暖啓動。相比冷啓動,暖啓動過程減少了對象初始化、佈局加載等工作,啓動時間更短。但啓動時,系統依然會展示閃屏頁,直到第一個 Activity 的內容呈現爲止。

熱啓動

相比暖啓動,熱啓動時應用做的工作更少,啓動時間更短。熱啓動產生的場景很多,常見如:用戶使用返回鍵退出應用,然後馬上又重新啓動應用。

如何進行優化?

參考今日頭條啓動優化流程

5.電量優化

耗電概念

其實大多數開發者對電量優化的重視程度極低,其實提到性能優化想到的就是內存優化,但我們不能忽視其他的優化,電量優化其實還是必要的,例如愛奇藝、優酷等等的視頻播放器以及音樂播放器。衆所周知,音樂和視頻其實是耗電量最大的。如果用戶一旦發現我們的應用非常耗電,不好意思,他們大多會選擇卸載來解決此類問題。爲此,我們需要進行優化。

如何優化

其實我們把上面那四種優化解決了,就是最好的電量優化。So,對於電量優化,我在此提一些建議:

  • 需要進行網絡請求時,我們需先判斷網絡當前的狀態。
  • 在多網絡請求的情況下,最好進行批量處理,儘量避免頻繁的間隔網絡請求。
  • 在同時有wifi和移動數據的情況下,我們應該直接屏幕移動數據的網絡請求,只有當wifi斷開時在調用,因爲,wifi請求的耗電量遠比移動數據的耗電量低的低。
  • 後臺任務要儘可能少的喚醒CPU。(比方說,鎖屏時,QQ的消息提示行就是喚醒了CPU。但是它的提示只有在你打開鎖屏或者進行充電時纔會進行提示。)

總結

對於Android的優化,這是一個必經之路,不光是要提升用戶的體驗,更是要自己的程序更加的健壯。如上的幾個分類是對Android的運行中的性能進行幾方面的優化方案。還有一些Android安裝包的優化方案,例如:APK減肥計劃、交互體驗的優化、動畫的優化等等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章