[Android]Android性能優化

安卓性能優化

性能優化的幾大考慮

  • Mobile Context
  • 資源受限
    + 內存,廣泛較小,512MB很常見,開發者的機器通常比用戶的機器高端
    + CPU,核心少,運算能力沒有全開
    + GPU,上傳大的紋理(texture),overdraw
  • 內存開銷大,會致使系統換入換出更頻繁,GC更頻繁,APP被kill、被重啓更頻繁,不只會消耗更多電量,並且GC會消耗大量時間,使得應用程序渲染速度低於60fps(GC耗時dalvik 10-20ms,ART 2-3ms)
  • 外部存儲與網絡,也是受限的,須要考慮資源的使用、網絡請求的優化
  • The Rules: Memory
  • Avoid Allocations in Inner Loops
  • Avoid Allocations When Possible
    + Cached objects
    + Object pools:注意線程安全問題
    + ArrayList v.s. 數組
    + Android collections classes:HashMap v.s. ArrayMap/SimpleArrayMap
    + Methods with mutated objects
    + Avoid object types when primitive types will do:SparseIntArray,SparseLongArray
    + Avoid arrays of objects
  • Avoid Iterators:顯式與隱式(foreach語句),會致使一個Iterator的分配,即使是空集合。
  • Avoid Enums
  • Avoid Frameworks and Libraries Not Written for Mobile Applications
  • Avoid Static Leaks
  • Avoid Finalizers
  • Avoid Excess Static Initialization
  • Trim caches on demand
  • Use isLowRamDevice:ActivityManager.isLowRamDevice()
  • Avoid Requesting a Large Heap
  • Avoid Running Services Longer than Necessary:BroadcastReceiver,IntentService
  • Optimize for Code Size
    + Use Proguard to strip out unused code
    + Carefully consider your library dependencies
    + Make sure to understand the cost of any code which is automatically generated
    + Prefer simple, direct solutions to problems rather than creating a lot of infrastructure and abstractions to solve those problems
  • The Rules: Performance
  • Avoid Expensive Operations During Animations and User Interaction
    動畫的每一幀渲染都是在UI線程的,若是有動畫的時候進行耗時操做,極可能致使動畫不流暢,耗時操做包括:
    + Layout:當動畫正在播放的時候,要避免改變View(延遲改變);同時選擇動畫也須要避免會觸發layout的動畫,例如translationX,translationY只會致使延遲的layout操做,而LayoutParams屬性,則會致使即時的layout。
    + Inflation:動畫過程當中避免inflate新的view,好比啓動新的activity,或者ListView滑動到不一樣type的區域。
  • Launch Fast
    + Avoid this problem by launching as fast as possible
    + Also, avoid initialization code in your Application object
  • Avoid Complex View Hierarchies
    + One approach to avoiding complex nested hierarchies is to use custom views or custom layouts in some situations; it may be cheaper for a single view to draw several pieces of text and icons rather than have a series of nested ViewGroups to accomplish this.
    + 結合的準則就是根據他們是否須要單獨和用戶完成交互(響應點擊事件等)
  • Avoid RelativeLayout Near the Top of the View Hierarchy
    RelativeLayout須要兩次measurement passes才能肯定佈局正確,嵌套RelativeLayout,是冪乘關係
  • Avoid Expensive Operations on the UI Thread
  • Minimize Wakeups
  • Develop for the Low End
  • Measure Performance
  • The Rules: Networking
  • Don’t Over-Sync:batch it up with other system requests with JobScheduler or GCM Network Manager.
  • Avoid Overloading the Server
  • Don’t Make Assumptions about the Network
  • Develop for Low End Networks
  • Design Back-End APIs to Suit Client Usage Patterns:相關數據一個請求分發完畢;不相關的數據分接口分發;客戶端應對獲取的數據具有足夠的信息;
  • The Rules: Language and Libraries
  • Use Android-Appropriate Data Structures: ArrayMap, SparseArray
  • Serialization
    + Parcelable:安卓系統IPC格式;把Parcel寫到磁盤是不安全的;解包方必須能訪問Parcel的類,不然將失敗;特定的類(Bitmap,CursorWindow)將被寫到SharedPreference中,而經過Parcel傳遞的只是文件的fd,存在性能優化的空間,可是也節約了內存;
    + Persistable Bundles:API 21引入,序列化爲XML,支持的類型比Parcel少,可是爲Bundle子類,某些場景方便處理;
    + Avoid Java Serialization:額外開銷更大,性能更差
    + XML and JSON:效率更低,複雜數據應考慮前述選項
  • Avoid JNI
    + 須要考慮多種處理器架構,指針用long保存
    + java->jni, jni->java調用開銷都很大,一次JNI調用作儘量多的工做
    + 內存管理,java對象管理jni對應對象的生命週期
    + 錯誤處理,在調用JNI以前檢查參數
    + 參數對象儘可能「傳值」調用,即:展開後傳遞,不要在JNI裏面使用指針訪問成員,避免JNI過程當中對象被回收
  • Prefer Primitive Types:內存、性能
  • The Rules: Storage
  • Avoid Hard-coded File Paths
  • Persist Relative Paths Only
  • Use Storage Cache for Temporary Files
  • Avoid SQLite for Simple Requirements
  • Avoid Using Too Many Databases
  • Let User Choose Content Storage Location
  • The Rules: Framework
  • Avoid Architecting Around Application Components
  • Services Should Be Bound or Started, Not Both
  • Prefer Broadcast over Service for Independent Events:Use broadcasts for delivering independent events; use services for processes with state and on-going lifecycle.
  • Avoid Passing Large Objects Through Binder
  • Isolate UI processes from Background Services
  • The Rules: User Interface
  • Avoid Overdraw
  • Avoid Null Window Backgrounds
    put the background drawable you want on the window itself with the windowBackground theme attribute and let those intervening containers keep their default transparent backgrounds.
  • Avoid Disabling the Starting Window(windowDisablePreview/windowBackground)
  • Allow Easy Exit from Immersive Mode
  • Set Correct Status/Navigation Bar Colors in Starting Window
  • Use the Appropriate Context
  • Avoid View-Related References in Asynchronous Callbacks
  • Design for RTL
  • Cache Data Locally
  • Cache User Input Locally
  • Separate Network and Disk Background Operations
  • Tools
  • Host Tools
    + Systrace
    • Alerts and the Frames, 提示很是詳盡,排查性能問題很方便
    • 可是不知道怎麼開啓,monitor開啓報錯,命令行開啓也報錯,是手機系統的緣由?
      + AllocationTracker
    • AS集成,點擊按鈕,對APP進行操做,再點擊按鈕結束,capture將在AS中打開,查看哪裏分配了內存,排查內存分配性能問題利器
      + Traceview
    • 根據Traceview的結果,查看耗時排名靠前的方法,分析緣由,提升性能
      + Hierarchyviewer
    • 查看當前view hierarchy中每一個view的繪製實踐,繪製較慢的該工具會給出提示(不一樣顏色)
      + MAT (Memory Analysis Tool)
    • 先經過heap dump把堆快照導出,再經過MAT進行分析(實踐?)
      + Memory Monitor
      + meminfo
  • On-device tools
    + StrictMode
    + Profile GPU rendering
    + Debug GPU overdraw
    + Animator duration scale
    + Screenrecord
    + Show hardware layer updates
  • Speed up your app
  • Rules
    + Always Measure
    + Use[Experience] Slow Device
    + Consider Trade-Offs
  • Memory tips
    + Bitmap's pixel format
    + Context Awareness
    + HashMap v.s. ArrayMap/Sparce*Array
  • LeakCanary
  • Alpha
    + TextView: setTextColor() instead of setAlpha()
    + ImageView: setImageAlpha() instead of setAlpha()
    + CustomView: handle alpha yourself by overriding onSetAlpha(), overriding hasOverlappingRendering()
  • Hardware Acceleration
    + view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
    + view.animate()....withLayer().start()
    + 硬件加速很是適用於動畫的渲染,可是也有須要注意的地方,ref
    • 硬件加速的原理是GPU會把View繪製的結果緩存起來,後續的更新只須要從新渲染便可,省去了View的繪製,以及繪製指令的傳輸部分,可是硬件加速一開始的時候有額外的初始化工做(緩存)
    • 若是View特別簡單,僅僅是一個單顏色區域,那硬件加速的額外開銷可能得不償失
    • 若是View在動畫過程當中不斷invalidate,或者其內容不斷變化,硬件加速的效果將大打折扣
    • 若是動畫發生在ViewGroup上,而其子View相對於ViewGroup也是在發生變化時,就不該該把硬件加速設置在ViewGroup上,由於動畫過程當中ViewGroup的內容是不斷變化的(子View也在不斷變化),而是應該把加速設置在各個子View上
    • GPU存儲空間有限,僅當有必要時才使用硬件加速
    • profile GPU rendering和show hardware layers updates是很好的效果評估工具

谷歌安卓團隊對於性能優化的建議

  • Android performance patterns系列視頻已經出到了第三季,國內也有安卓大神整理翻譯的中文文字版,但就像讀書同樣,大神寫了完整的書,看的時候仍是要作個筆記的。如下只是針對自身狀況的筆記,僅供參考。
  • S1E0: Render Performance
  • 60 fps, 16 ms每幀
  • S1E1: Understanding Overdraw
  • 開發者選項:Show GPU Overdraw
  • 避免設置多重背景
  • S1E2: Understanding VSYNC
  • Refresh Rate:表明了屏幕在一秒內刷新屏幕的次數,這取決於硬件的固定參數,例如60Hz
  • Frame Rate:表明了GPU在一秒內繪製操做的幀數,例如30fps,60fps
  • 當Refresh rate和Frame rate不一致時,將會發生tearing效果:屏幕的內容被分紅了上下兩部分,分別來自兩幀的內容
    + 原理簡述:視頻(動畫)是由靜態幀快速切換達到的效果(例如60 fps),而每幀是一個圖片,其內容就是一個像素矩陣,顯示屏繪製每幀的時候,是逐行繪製該像素矩陣的,理想狀況下,顯示屏繪製完一幀的像素以後,去獲取下一幀的像素進行繪製,但若是顯示屏繪製第一幀的像素繪製到一半,保存幀像素矩陣的buffer被寫入了第二幀的像素矩陣,而顯示屏並不知道,仍會接着從上一行往下繪製,就會致使繪製出來的圖像上部分屬於上一幀,下部分屬於下一幀,即tearing效果。
  • VSync,用來同步GPU和屏幕繪製,在GPU載入新幀以前,要等待屏幕繪製完成上一幀的數據
    + 還有更多關於圖像渲染的內容,三重緩衝等,須要擴展閱讀
  • 幀率大於刷新頻率(60 fps)時,顯示很流暢;幀率小於60 fps時,會顯示重複幀;幀率忽然從大於60 fps降到低於60 fps時,用戶就會感覺到卡頓的發生了;
  • S1E3: Tool: Profile GPU Rendering
  • 開發者選項:Profile GPU Rendering, On screen as bars
  • 兩個區域(虛擬鍵盤設備會有三個區域),從上到下分別表示:狀態欄繪製、主窗口繪製、虛擬鍵盤區域繪製
  • 三種顏色
    + 藍色:draw time,建立、更新display list所消耗的時間;onDraw函數中使用Canvas調用的draw*函數的執行時間;convert to GPU description, cache as display list;
    • 藍色太高,可能由於大量view被invalidate,須要重繪,或者是onDraw方法的邏輯過於複雜,執行時間長
      • 紅色:execute time,Android 2D renderer執行display list所消耗的時間(經過Open GL接口,使用GPU繪製);自定義View越複雜,GPU渲染所需時間越長;
    • 紅色太高,緣由極可能就是View的構成太複雜;極高的峯值,多是由於從新提交了視圖繪製形成的,並不是view被invalidate,而是相似於View旋轉這樣的變化,須要先清空原有區域,再從新繪製;
      • 橙色:process time,CPU通知GPU渲染結束消耗的時間,同步調用
    • 橙色太高,多是View太複雜,渲染須要太多時間
  • S1E4: Why 60fps? 常識,12 fps近乎於人手快速翻書,24 fps是電影的經常使用幀率,60 fps用於表現絢麗的動畫效果
  • S1E5: Android, UI and the GPU
  • Resterization(柵格化):繪製Button,Shape,Path,Text,Bitmap等組件最基礎的操做;柵格化就是將這些組件的內容拆分到不一樣的像素上進行顯示;柵格化很耗時,引入GPU就是爲了加快柵格化操做;
  • CPU負責把UI組件計算成Polygons,Texture紋理,而後交給GPU進行柵格化渲染
  • 所以須要在CPU和GPU之間傳遞數據,OpenGL ES能夠把那些須要渲染的紋理Hold在GPU Memory裏面,在下次須要渲染的時候直接操做。若是更新了GPU所hold住的紋理內容,以前保存的狀態就丟失了,將致使繪製變慢。
  • 在安卓系統中,由主題提供的資源(Bitmaps,Drawables等)都是一塊兒打包到統一的Texture紋理當中,而後再傳遞到GPU裏面,這意味着每次你須要使用這些資源的時候,都是直接從紋理裏面進行獲取渲染的,速度將會更快。
  • 也有更復雜的組件,例如顯示圖片的時候,須要先通過CPU的計算加載到內存中,而後傳遞給GPU進行渲染。文字的顯示更加複雜,須要先通過CPU換算成紋理,而後再交給GPU進行渲染,回到CPU繪製單個字符的時候,再從新引用通過GPU渲染的內容。動畫則是一個更加複雜的操做流程。
  • S1E6: Invalidations, Layouts, and Performance
  • DisplayList:包含須要GPU繪製到屏幕上的數據信息
  • 在某個View第一次被渲染時,會建立DisplayList,當這個View要顯示到屏幕上時,會執行GPU的繪製指令來進行渲染。若是後續有移動這個View的位置等操做而須要再次渲染這個View時,只須要額外操做一次渲染指令便可。若是修改了View中的某些可見組件,將須要進行建立DisplayList、執行渲染指令整個過程。
  • 上述步驟可簡化爲下圖,須要的時間取決於View的複雜度,View重繪引發的View hierarchy的變化的複雜度

這裏寫圖片描述

  • 可用工具:Profile GPU Rendering查看渲染的表現性能;Show GPU view updates查看視圖更新的操做;HierarchyViewer查看界面佈局結構;
  • 目標/要點:使佈局扁平化,移除非必要組件,避免使用嵌套layout(尤爲是根節點方向的RelativeLayout和有weight的LinearLayout)
  • S1E7: Overdraw, Cliprect, QuickReject
  • 標準組件(或其組合)的Overdraw可使用工具來檢測、消除;同時系統也會避免繪製徹底不可見的組件;
  • 徹底自定義組件(重寫onDraw方法),上述方案將沒法實現;可經過canvas.clipRect()來幫助系統識別可見區域,徹底在矩形外面的內容(組件)將不會繪製、渲染,但有部分在矩形內的仍會繪製、渲染;
  • canvas.quickreject()函數能夠判斷是否和某個矩形相交,若是不相交,則能夠直接跳過;
  • 性能優化總原則之一:先測量效果,發現問題再尋找根源,嘗試改進後要再次測量效果進行對比
  • S1E8: Memory Churn and performance
  • Android的堆內存分代回收模型以下圖示
    這裏寫圖片描述
  • GC時會暫停全部其餘線程,致使GC頻繁的可能緣由
    + Memory Churn(內存抖動),大量對象被建立,而後當即被銷燬,例如在onDraw等函數中建立對象
    + 瞬間產生大量的對象會嚴重佔用Young Generation的內存區域,當達到閥值,剩餘空間不夠的時候,也會觸發GC。同時可能觸發其餘區域(代)的GC
  • 使用Android studio的Memory Monitor,能夠觀察是否存在內存抖動,Memory monitor集成了Allocation Tracker,使用Allocation Tracker能夠查看每一個線程的內存分配狀況,能夠具體到某個函數的某行代碼
  • 日常須要注意的是:onDraw等這樣會被高頻率反覆調用的函數、循環體內部等,避免建立新對象
  • S1E9: Garbage Collection in Android
  • 安卓系統使用的虛擬機是三代模型:Young, old, permanent;越日後每代GC時間越長;
  • 應該避免頻繁GC
  • S1E10: Performance Cost of Memory Leaks
  • 一個典型的Activity泄露場景:Activity類內部的非靜態Handler子類(或匿名類)實例
  • LeakCanary工具是檢測內存泄漏的好工具
  • S1E11: Memory Performance
  • 工具集:Memory Monitor:觀察是否存在內存抖動;Allocation Tracker:追蹤內存分配狀況;Heap Tool:查看內存快照,分析可能泄露的對象;
  • S1E12: Tool - Memory Monitor,無甚可記
  • S1E13: Battery Performance
  • 儘可能減小喚醒屏幕的次數與持續的時間,使用WakeLock來處理喚醒的問題,可以正確執行喚醒操做並根據設定及時關閉操做進入睡眠狀態。
  • 某些非必須立刻執行的操做,例如上傳歌曲,圖片處理等,能夠等到設備處於充電狀態或者電量充足的時候才進行。
  • 觸發網絡請求的操做,每次都會保持無線信號持續一段時間,咱們能夠把零散的網絡請求打包進行一次操做,避免過多的無線信號引發的電量消耗。
  • Battery Historian Tool:查看APP電量消耗狀況
  • JobScheduler API:對任務進行定時處理,例如等到手機處於充電狀態,或鏈接到WiFi時處理任務
  • JobScheduler在API 21引入,Google play service有backport: GcmNetworkManager,也有第三方backport
  • S1E14: Understanding Battery Drain on Android
  • 使用WakeLock或者JobScheduler喚醒設備處理定時的任務以後,必定要及時讓設備回到初始狀態。
  • 每次喚醒無線信號進行數據傳遞,都會消耗不少電量,它比WiFi等操做更加的耗電
  • S1E15: Battery Drain and WakeLocks
  • WakeLock使用非精準定時器,容許系統爲不一樣應用的wake lock請求進行打包處理,節約電量消耗
  • JobScheduler API還能作更多的事情,例如等到充電,或者鏈接上wifi時處理任務html

  • S2E1: Battery Drain and Networking
  • 捆綁網絡請求,按批次執行,可下降激活網絡、等待休眠過程的均攤成本;使用JobScheduler能夠實現更多的優化策略;
  • 預取與壓縮
  • Android Studio中的Networking Traffic Tool
  • adb工具查看電量消耗:adb shell dumpsys batterystats > xxx.txt, python historian.py xxx.txt > xxx.html
  • 利用BatteryManager在代碼中檢測是否正在充電(也可以使用JobScheduler來作)
  • S2E2: Wear & Sensors
  • 首先咱們須要儘可能使用Android平臺提供的既有運動數據,而不是本身去實現監聽採集數據,由於大多數Android Watch自身記錄Sensor數據的行爲是有通過作電量優化的。
  • 其次在Activity不須要監聽某些Sensor數據的時候須要儘快釋放監聽註冊。
  • 還有咱們須要儘可能控制更新的頻率,僅僅在須要刷新顯示數據的時候才觸發獲取最新數據的操做。
  • 另外咱們能夠針對Sensor的數據作批量處理,待數據累積必定次數或者某個程度的時候才更新到UI上。
  • 最後當Watch與Phone鏈接起來的時候,能夠把某些複雜操做的事情交給Phone來執行,Watch只須要等待返回的結果。
  • S2E3: Smooth Android Wear Animation
  • 動畫優化例1:在一個圓形的鐘表圖上,咱們把時鐘的指針摳出來當作單獨的圖片進行旋轉會比旋轉一張完整的圓形圖的所造成的幀率要高56%
  • 動畫優化例2:把背景圖片單獨拎出來設置爲一個獨立的View,經過setLayerType()方法使得這個View強制用Hardware來進行渲染
  • 動畫優化例3:使用PropertyAnimation或者ViewAnimation來操做實現,Android系統會自動對這些Animation作必定的優化處理
  • S2E4: Android Wear Data Batching
  • 僅僅在真正須要刷新界面的時候才發出請求
  • 儘可能把計算複雜操做的任務交給Phone來處理
  • Phone僅僅在數據發生變化的時候才通知到Wear
  • 把零碎的數據請求捆綁一塊兒再進行操做
  • S2E5: Object Pools
  • 避免對象的頻繁建立與銷燬,減小內存抖動;避免重量級對象的建立與銷燬,下降建立開銷;
  • lazy分配 v.s. 預分配
  • 慎用,須要手動釋放,謹防內存泄漏;爲了確保全部的對象可以正確被釋放,須要保證加入對象池的對象和其餘外部對象沒有互相引用的關係。
  • S2E6: To Index or Iterate?
  • for indexjava

int size = list.size();
for (int i = 0; i < size; i++) {
  Object object = list.get(i);
  ...
}
  • Iterator
for (Iterator it = list.iterator(); it.hasNext(); ) {
  Object object = it.next();
  ...
}
  • for simplified
for (Object object : list) {
  ...
}
  • 性能對比
Fcn Time taken(ms)
for index (ArrayList) 2603
for index (Vector) 4664
for simple (ArrayList) 5133
Iterator (ArrayList) 5142
for simple (Vector) 11783
Iterator (Vector) 11778
  • S2E7: The Magic of LRU Cache
  • 安卓系統提供了LruCache類,已實現LRU算法
  • 慎用,手動釋放緩存的對象,謹防內存泄漏
  • S2E8: Using LINT for Performance Tips,無甚可記
  • S2E9: Hidden Cost of Transparency
  • 一般來講,對於不透明的View,顯示它只須要渲染一次便可,但是若是這個View設置了alpha值,會至少須要渲染兩次
  • 在某些狀況下,一個包含alpha的View有可能會觸發該View在HierarchyView上的父View都被額外重繪一次
  • 在動畫開始時,setLayerType(View.LAYER_TYPE_HARDWARE, null),動畫結束後,setLayerType(View.LAYER_TYPE_NONE, null);在API >= 16時,能夠只調用ViewPropertyAnimator.alpha(0.0f).withLayer()接口便可;
  • 使用shadow時,重寫View的hasOverlappingRendering()接口,返回false;
  • 只有當肯定瓶頸是這部分view的渲染時,纔有必要這樣優化;
  • S2E10: Avoiding Allocations in onDraw(),記住,實踐便可
  • S2E11: Tool: Strict Mode
  • 開發者選項Strict Mode,若是存在潛在ANR隱患(主線程磁盤IO,網絡請求等),屏幕會閃現紅色
  • 也有代碼的API,StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()...build());StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()...build());,若是有隱患,屏幕會閃爍,而且有log輸出
  • S2E12: Custom Views and Performance,避免如下問題:
  • Useless calls to onDraw(),僅在View內容發生變化時才調用View.invalidate();儘可能使用ClipRect等方法來提升繪製的性能;
  • Useless pixels,減小繪製時沒必要要的元素,對於那些不可見的元素不要繪製;
  • Wasted CPU cycles,不在屏幕上的元素,可使用Canvas.quickReject把他們給剔除;另外儘可能使用GPU來進行UI的渲染,這樣可以極大的提升程序的總體表現性能;
  • S2E13: Batching Background Work Until Later,上文有涉及
  • S2E14: Smaller Pixel Formats
  • dalvik虛擬機不會自動進行內存整理
  • Android爲圖片提供了4種解碼格式:ARGB_8888, RGB_565, ARGB_4444, ALPHA_8,可是實際測試的時候,若是圖片不變,顯示圖片的View區域不變,也不限制Bitmap只有View那麼大,單純設置bitmapOption.inPreferredConfig好像沒什麼用
  • 能夠經過將解碼的Bitmap大小設置爲View大小的兩倍(單位是dp),實測內存基本能夠降到1/16(固然前提是圖片比View大不少),並且視覺效果基本同樣
  • S2E15: Smaller PNG Files,如下建議結合微信安卓團隊的分享
  • 沒有透明度的文件,均可以用jpg
  • 對於體積特別大(超過50k)的圖片資源能夠考慮有損壓縮,jpg採用優圖壓縮,png嘗試採用pngquant壓縮,輸出視覺判斷是否可行;
  • 對assets中的圖片資源也使用aapt的crunch作圖片預處理;
  • crunch有可能會使圖片變大,在這種狀況,咱們能夠替換成原圖。須要注意的是對於.9.png,因爲crunch過程當中去除了黑邊,因此不能替換;
  • 對於沒有透明區域的png圖片,能夠轉成jpg格式。
  • Webp:既保留png格式的優勢,又可以減小圖片大小
  • S2E16: Pre-scaling Bitmaps
  • bitmapOption.inSampleSize屬性,可等比例縮放圖片,並且不會加載原圖到內存
  • inScaled,inDensity,inTargetDensity的屬性來對解碼圖片作處理
  • inJustDecodeBounds,只會先讀取圖片尺寸,不會加載到內存
  • S2E17: Re-using Bitmaps
  • 設置了inbitmapOption.Bitmap後,系統會嘗試使用已有的Bitmap來保存新的圖片,避免內存的反覆建立,可是這一方法有些不足:
    + 在SDK 11 -> 18之間,重用的bitmap大小必須是一致的
    + 從SDK 19開始,新申請的bitmap大小必須小於或者等於已經賦值過的bitmap大小
    + 新申請的bitmap與舊的bitmap必須有相同的解碼格式
  • 能夠結合對象池,給不一樣解碼格式,不一樣大小的圖片準備不一樣的Bitmap並進行復用,不過這樣作難度較大
  • 開源圖片加載庫GlideFresco
  • S2E18: The Performance Lifecycle
  • Gather:測試收集數據
  • Insight:分析數據
  • Action:解決問題
  • 再次進行測試,驗證效果
  • S2E19: Tools not Rules,上述建議須要靈活應用
  • S2E20: Memory Profiling 101,上文已述python

  • S3E1: Fun with ArrayMaps; S3E2: Beware Autoboxing; S3E3: SparseArray Family Ties
  • Java原生容器類只支持對象,primitive類型會自動裝箱/拆箱,存在必定時間開銷,並且沒有放入KV時也會分配內存,空間開銷會大不少
  • 安卓系統提供了功能相似的容器實現:ArrayMap,空間開銷更低
    + 使用兩個數組實現,一個保存排好序的key的hash值,另外一個保存key-value
    + 查找時對hash數組進行二分查找,找到後訪問key-value數組,若是對應位置key不一樣,則是由於有碰撞,則向上向下兩路查找
    + 插入、刪除涉及到數組元素的插入與刪除,效率低一些
    + 數據量在1K個key-value對時,時間性能幾乎無差別,可是空間性能提高不少
    + 仍使用對象做爲KV,僅僅是避免不用的內存分配
  • 避免混用原生類型與對應box類型,自動裝箱/拆箱會涉及到對象的建立,時間、空間均有必定開銷
  • SparseBooleanArray(int -> boolean), SparseIntArray(int -> int), SparseLongArray(int -> long), LongSparseArray(long -> Object), 千如下時能夠考慮使用
  • S3E4: The price of ENUMs
  • enum會佔用更多的磁盤空間(編譯的class文件)和運行時內存
  • S3E5: Trimming and Sharing Memory
  • 能夠處理onLowMemory()onTrimMemory()
  • onTrimMemory()的回調能夠發生在Application,Activity,Fragment,Service,Content Provider。
  • 從Android 4.4開始,ActivityManager提供了isLowRamDevice()的API,一般指的是Heap Size低於512M或者屏幕大小<=800*480的設備。
  • S3E6: DO NOT LEAK VIEWS
  • 當屏幕發生旋轉的時候,activity很容易發生泄漏
  • 異步回調也很容易發生泄漏
  • 內部非靜態Handler子類實例也容易發生泄漏
  • 避免使用Static對象
  • 避免把View添加到沒有清除機制的容器裏面
  • S3E7: Location & Battery Drain
  • 頻率越高,耗電越大
  • 精度越高,耗電越大
  • setFastestInterval(),LocationRequest.setPriority()
  • S3E8: Double Layout Taxation
  • 避免在高層級節點使用會兩次layout的Layout:RelativeLayout,使用weight的LinearLayout/GridLayout
  • 扁平化view hierarchy,減小層級
  • ListView/RecyclerView這樣的item view裏面也要尤爲注意渲染性能
  • 在任什麼時候候都請避免調用requestLayout()的方法,由於一旦調用了requestLayout,會致使該layout的全部父節點都發生從新layout的操做
  • 可使用Systrace來跟蹤特定的某段操做
  • S3E9: Network Performance 101
  • 打包批量發送請求
  • 適當預取
  • 適當壓縮
  • S3E10: Effective Network Batching
  • S3E11: Optimizing Network Request Frequencies
  • S3E12: Effective Prefetchingandroid

Square團隊的建議

  • Eliminating Code Overhead by Jake Wharton
  • CPU
    + Do not nest multi-pass layouts: RelativeLayout, LinearLayout with layout_weight...
    + Lazily compute complex data when needed
    + Cache heavy computational results for re-use
    + Consider RenderScript for performance
    + Keep work off main thread
  • Memory
    + Use object pools and caches to reduce churn (judiciously)
    + Be mindful of the overhead of enums
    + Do not allocate inside the draw path
    + Use specialized collections instead of JDK collections when appropriate (SparceArray...)
  • I/O
    + Batch operations with reasonable back-off policies
    + Use gzip or binary serialization format
    + Cache data offline with TTLs for reloading
    + Use JobScheduler API to batch across OS
  • Spectrum of optimizations, not binary
  • Do not blindly apply to everything, only appropriate
  • Multiple micro-optimizations can improve like macro
  • ArrayList分配:會有一個默認初始值,之後空間不夠時按倍增策略進行擴展
    + 若是建立時就知道其大小,則能夠new一個已知容量的ArrayList,避免後面擴容、數據複製的成本
    +
  • StringBuilder:一樣的,也能夠先給一個預估的大小,而後直接初始化該大小的StringBuilder;安卓開發build時會自動把String的拼接操做轉化爲StringBuilder實現,然而這種自動的轉換未必高效;
    + 例子
    java for (int x = 0; x < valueCount; x++) { cleanFiles[x] = new File(directory, key + "." + x); dirtyFiles[x] = new File(directory, key + "." + x + ".tmp"); }
    ===>>>
    java StringBuilder b = new StringBuilder(key).append("."); int truncateTo = b.length(); for (int x = 0; x < valueCount; x++) { b.append(x); cleanFiles[x] = new File(directory, b.toString()); b.append(".tmp"); dirtyFiles[x] = new File(directory, b.toString()); b.setLength(truncateTo); }
  • 其餘
    + 對函數的調用(尤爲是虛函數、接口函數)結果,若是同一個做用域中有屢次調用,且結果肯定不變,應該將他們轉化爲一次調用:for (int i = 0, size = list.size(); i < size; i++)
    + 對集合的遍歷,不要使用語法糖,會有額外開銷(Iterator建立、虛函數調用等)

NimbleDroid的建議

  • 性能優化的流程
    這裏寫圖片描述
  • Recommendation 1: limit app startup to 2 seconds
  • Recommendation 2: eliminate hung methods
  • Recommendation 3: measure as often as you can,怎麼、什麼粒度的profiling呢?
  • Recommendation 4: know a set of common issues
  • ClassLoader.getResourceAsStream()
  • Recommendation 5: avoid surprises in 3rd-party SDKs
相關文章
相關標籤/搜索