Android性能優(yōu)化總結(jié)

最近大半個月都在做app的優(yōu)化,最主要是從apk包大小、界面過度繪制、掉幀、內(nèi)存抖動、主線程IO這幾個方面來入手的。相比開發(fā)新功能,做優(yōu)化真的是更費腦力和心神,因為也許你做了大量的修改和優(yōu)化操作,能看見的效果卻微乎其微,但又不得不做。我想對于大多數(shù)Android開發(fā)者來說,開發(fā)出一款app并不難,但是開發(fā)出一款高性能體驗棒的app卻并不是每個開發(fā)者都能做到的。在開發(fā)過程中,可能會因為各種各樣的原因會使你開發(fā)出來的app性能不佳,體驗很差,開發(fā)者寫代碼的水平肯定是最重要的因素,另外還有一些第三方SDK也可以能會引起你的app出現(xiàn)問題。

今天抽空把這大半個月以來優(yōu)化的過程和心得記錄一下。

apk包大小

無論是個人開發(fā)者自己開發(fā)的產(chǎn)品還是公司開發(fā)的產(chǎn)品,盡可能減小apk包的大小可以大大提高app的下載轉(zhuǎn)化率,所以優(yōu)化過程中apk體積是開發(fā)者必須要注意的一點。我們先看看apk包是由哪些部分組成的。我們可以使用Android Studio自帶的功能Build->Analyze APK,下圖是微信的apk組成:

wechat_apk
  • lib 存放app所需的native庫文件
  • classes.dex 開發(fā)者編寫的java文件最后都會轉(zhuǎn)化成dex文件運行在Android虛擬機上
  • assets 存放需要保持原始文件的資源文件
  • res 存放所有的資源文件
  • resource.arsc 所有資源文件的id映射
  • META-INF 簽名校驗文件
  • AndroldManifest.xml Android應用全局配置文件
  • 其它一些配置文件和第三方庫生成的文件

刪除無用資源

首先我從資源文件入手。我們可以使用Android Studio中的工具搜索項目中沒有使用的的資源文件:

run-inspection-by-name

然后通過unused resources這個功能來查找:

unused-resources

查找完成后會把整個項目中未被使用的資源列出來,但是這個清理的時候我們需要注意下,有些資源文件可能是在你的項目中有用到的,但是資源id未出現(xiàn)在項目中的,比如你在代碼中是通過資源名來獲取這個資源而不是通過資源id,這種情況下的資源就不能刪了,否則就會出錯。另外一些第三方庫的資源文件也要小心誤刪。

關(guān)于保留幾種屏幕分辨率的資源文件

隨著目前手機市場的發(fā)展,屏幕越來越大,分辨率越來越高,但是基于成本的考慮,仍然還有一些相對較低的分辨率的機型,而我們的app是適配所有分辨率的機型,還是針對一些主流的機型做適配,這取決于我們app所適用的人群范圍。我們UI設計師一般是設計xxhdpi(480dpi)和xhdpi(320dpi)兩套,基本上可以滿足絕大部分的手機屏幕了,如果你的app對apk體積有極高的要求,那么你也可以只選擇一套分辨率的素材。

資源壓縮

圖片壓縮:一般UI給我們的圖片都可以壓縮,我一般采用tinypng在線壓縮,支持png/jpg格式,為了避免失真,不要對同一張圖片壓縮多次。如果是Mac系統(tǒng)下,還可以選擇ImageOptim。

使用SVG圖片:SVG圖片即矢量圖,簡單的說,就是縮放不失真的圖像格式。使用矢量圖的好處有很多,可被非常多的工具讀取和修改(比如記事本)。SVG與JPEG和GIF圖像比起來,尺寸更小,且可壓縮性更強。SVG是可伸縮的,SVG圖像可在任何的分辨率下被高質(zhì)量地打印,SVG可在圖像質(zhì)量不下降的情況下被放大,SVG圖像中的文本是可選的,同時也是可搜索的(很適合制作地圖),SVG可以與Java技術(shù)一起運行,SVG文件是純粹的XML。使用Android Studio也可以將下載的svg格式的矢量圖轉(zhuǎn)化為xml格式。比如我一般在阿里的Iconfont上下載一些簡單的圖標,它可以選擇svg格式下載,然后在Android Studio的drawable文件夾右鍵NEW->Vector Asset:

然后會進到下面這個界面:

其中Asset Type類型中Material Icon是通過系統(tǒng)自帶的一些符合Material Design設計的icon來制作,Local file就是通過我們自己下載的svg文件來制作,完成后svg格式的文件就轉(zhuǎn)化成xml格式保存在我們的目標文件夾里了。當然普通的ImageView是無法使用矢量圖的,必須使用v7包下的AppCompatImageView,而且不能使用src屬性,必須是app:srcCompat屬性才可以。

用jpg代替png:因為jpg沒有alpha通道,所以文件更小,比較適合于不需要透明度的圖片。

用shape、color來代替圖片:如果是漸變背景或者純顏色的控件背景,都可以使用shape或者color做背景,一個xml文件相比一張位圖要小得多。

使用混淆

混淆除了代碼壓縮代碼混淆的功能還有資源壓縮的作用,在app module的build.gradle文件中配置shrinkResources true和minifyEnabled true這兩個參數(shù),就可以達到混淆壓縮的目的。關(guān)于混淆,參看這篇文章 Android代碼混淆與進階

native庫文件

native庫都是為了支持不同架構(gòu)的CPU,雖然native庫文件是占整個apk體積最大的部分,減少對其中一些CPU架構(gòu)的支持可以很直觀的看到變化。關(guān)于so文件的知識和它的適配,可以看看下面幾篇文章,會有很大幫助。

過度繪制

首先我們得知道,過度繪制這個概念,到底什么是過渡繪制?UI過度繪制簡單的來說是指在一個界面中有很多元素,但是我們只需要更新某一小塊的元素,app卻把所有的元素都刷新一遍,這就造成過度繪制。過度繪制會造成GPU資源浪費,引起我們的app頁面卡頓,掉幀現(xiàn)象。

overdraw_options_view

上圖是UI界面的過度繪制的情況,不同的顏色代表被過度繪制的次數(shù),開發(fā)者可以在手機設置的開發(fā)者選項中將“調(diào)式過度繪制”選項開啟,然后就可以看到界面上顯示不同的顏色了。我們優(yōu)化的目的就是盡可能讓界面上只看到藍色或綠色,盡量不要出現(xiàn)大量的紅色。

怎么做才能減少紅色部分?盡量將我們的layout布局層次簡單化,移除Window默認的background,移除layout中非必須的background等,UI布局層次不應該過多,否則系統(tǒng)繪制UI時會占用較多時間,引起掉幀。

更詳細的優(yōu)化方法請看

內(nèi)存抖動及優(yōu)化

內(nèi)存抖動是因為在短時間內(nèi)大量的對象被創(chuàng)建又馬上被釋放。瞬間產(chǎn)生大量的對象會嚴重占用Young Generation的內(nèi)存區(qū)域,當達到閥值,剩余空間不夠的時候,會觸發(fā)GC從而導致剛產(chǎn)生的對象又很快被回收。即使每次分配的對象占用了很少的內(nèi)存,但是他們疊加在一起會增加Heap的壓力,從而觸發(fā)更多其他類型的GC。這個操作有可能會影響到幀率,并使得用戶感知到性能問題。如果你在Memory Monitor里面查看到短時間發(fā)生了多次內(nèi)存的漲跌,這意味著很有可能發(fā)生了內(nèi)存抖動。同時我們還可以通過Allocation Tracker來查看在短時間內(nèi),同一個棧中不斷進出的相同對象。這是內(nèi)存抖動的典型信號之一。當你大致定位問題之后,接下去的問題修復也就顯得相對直接簡單了。例如,你需要避免在for循環(huán)里面分配對象占用內(nèi)存,需要嘗試把對象的創(chuàng)建移到循環(huán)體之外,自定義View中的onDraw方法也需要引起注意,每次屏幕發(fā)生繪制以及動畫執(zhí)行過程中,onDraw方法都會被調(diào)用到,避免在onDraw方法里面執(zhí)行復雜的操作,避免創(chuàng)建對象。對于那些無法避免需要創(chuàng)建對象的情況,我們可以考慮對象池模型,通過對象池來解決頻繁創(chuàng)建與銷毀的問題,但是這里需要注意結(jié)束使用之后,需要手動釋放對象池中的對象。

啟動時間(冷啟動,暖啟動,熱啟動)

app的啟動分為三種狀態(tài),冷啟動即應用從零開始加載運行,而其它狀態(tài)則是應用從后臺運行回到前臺運行。

冷啟動狀態(tài):系統(tǒng)不存在該應用的進程,啟動應用才創(chuàng)建出應用的進程。冷啟動一般指的就是應用在開機后或者被系統(tǒng)停止后的第一次啟動過程。因為系統(tǒng)和應用在冷啟動時需要做更多的工作,所以減少它的啟動時間的難度是最大的。

冷啟動初始時,系統(tǒng)完成三個任務:

  • 啟動和加載應用(這里泛指的是應用本身)
  • 創(chuàng)建應用的專屬進程
  • 啟動后立刻顯示啟動視圖(通常是個空白屏)

一旦系統(tǒng)創(chuàng)建了應用的專屬進程,該進程開始創(chuàng)建應用:

  • 創(chuàng)建應用對象
  • 啟動主線程 (MainThread)
  • 創(chuàng)建 Launcher Activity
  • 加載視圖 (Inflating views)
  • 渲染布局 (Laying out)
  • 執(zhí)行初始繪制

當應用完成了第一次繪制,系統(tǒng)進程就把當前顯示的啟動視圖切換為應用界面,用戶就可以使用應用了。

應用進程的啟動流程可以分為:Application的創(chuàng)建和Launcher Activity的創(chuàng)建,Application的創(chuàng)建是從Application.onCreate()開始的,重寫onCreate()方法通常我們會在這里完成一些通用組件和第三方SDK的初始化操作。接著應用程序生成主線程,并開始創(chuàng)建Launcher Activity。創(chuàng)建Activity的過程分為初始化、調(diào)用構(gòu)造方法、調(diào)用當前生命周期的回調(diào)方法。通常onCreate()方法對加載時間的影響最大,因為它要執(zhí)行加載、渲染和初始化Activity所需要的對象等開銷最大的任務,如果Activity的布局過于復雜,那么就很可能會導致啟動性能問題。

暖啟動狀態(tài):應用程序的暖啟動與冷啟動類似,但比冷啟動開銷低。在暖啟動中,系統(tǒng)只需要把 Activity 切換到前臺運行。如果應用的該 Activity 之前駐留在內(nèi)存中,那么應用程序就不用重新初始化對象和渲染布局。但是,如果由于響應了低內(nèi)存事件,例如在 onTrimMemory() 方法中清除了資源對象,那么這些對象就需要在熱啟動時重新創(chuàng)建。

熱啟動狀態(tài):熱啟動為冷啟動的過程操作的子集,而且開銷比暖啟動稍小。以下這些情況可以認為是熱啟動:

  • 用戶退出應用,但隨后重新啟動它。應用的進程還在運行,但應用必須重新從 onCreate() 開始創(chuàng)建 Activity。
  • 系統(tǒng)從內(nèi)存中清除了應用(非用戶主動),然后用戶重新啟動它。進程和 Activity 需要重新啟動,但 onCreate() 將接收到保存狀態(tài)的 Bundle。事實上,savedInstanceState 在用戶未主動銷毀 Activity 時系統(tǒng)就會調(diào)用。

啟動時間的優(yōu)化:

一般我們優(yōu)化啟動時間都是基于冷啟動時間的,所以主要是創(chuàng)建應用和創(chuàng)建Activity這兩部分所花時間的優(yōu)化,在Application.onCreate()方法中,要盡量避免創(chuàng)建太多臨時變量、執(zhí)行IO操作、以及反序列化操作、多重for循環(huán)等。而在創(chuàng)建Launcher Activity過程中,布局視圖要盡量層次簡單,讓繪制過程盡量短,加載大量資源時可以選擇懶加載方式。

關(guān)于啟動視圖:

之前說過當應用啟動后而且Launcher Activity的布局還沒有被渲染完成時,會出現(xiàn)一個空白屏,如果Launcher Activity的布局非常復雜,渲染消耗的時間很長,那用戶可能會感知到這個空白屏的存在,體驗上來說,是很不友好的,那么怎么解決這個問題呢?我們可以使用windowBackground這個屬性,一般我們會設置成一張圖片,然后在AndroidManifest.xml中的application節(jié)點的Theme中加上這個屬性。這樣做之后,當應用被點擊后立馬就可以看到這張圖片,而不是一個空白屏了。

主線程IO

如果在主線程中有讀寫文件等這些耗時的操作,就會阻塞主線程,影響app的性能。所以在主線程中,不要進行IO操作,也不要進行for循環(huán)創(chuàng)建大量的對象。如果必須要操作IO,也要放在子線程中或者Service中進行,以保證主線程不會被阻塞。

64K 引用限制

關(guān)于64k引用限制,官方的說明文檔:配置方法數(shù)超過 64K 的應用,規(guī)避64k限制的辦法就是采用Dalvik可執(zhí)行文件分包。具體的配置方法請看文檔說明。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容