鍍金池/ 教程/ Android/ 安卓性能優(yōu)化
Launch mode 和 Intent flags專題
Canvas & Drawables
UTAustinX_UT.9.01x: Effective Thinking Through Mathematics
《JavaScript 語言精粹》
Memory leak專題
React基礎(chǔ)
《Test Driven Development: By Example》一書
Developer tools
安卓開發(fā)技能樹
<a rel="nofollow" href="https://mp.weixin.qq.com/s?__biz=MzA3NDM
Best Practices for Interaction and Engagement
各個安卓版本引入的主要新特性
Building Apps with Connectivity &amp; the Cloud
List.toArray()再強轉(zhuǎn)是一定會失敗的
深入Android frameworks
Google dev 100 days系列視頻
Building Apps with Contacts &amp; Sign-In
關(guān)系型數(shù)據(jù)庫設(shè)計范式
《App研發(fā)錄》一書
REST API設(shè)計
Google IO 2015摘要
自定義View/ViewGroup以及高性能實現(xiàn)自定義UI
安卓系統(tǒng)點擊事件處理
《50 Android Hacks》一書
Building Apps with Content Sharing
Flux基礎(chǔ)
<a rel="nofollow" href="http://developer.android.com/training/in
依賴注入(以Dagger 2為例)
Java同步機制
Java對象內(nèi)存的使用情況
JSR133(Java memory model)
Google官方Material Design手冊(<a rel="nofollow" href="http://develop
Futurice公司安卓團隊的建議
安卓性能優(yōu)化
  • 1.
Best Practices for Performance
<a rel="nofollow" href="http://www.vogella.com/tutorials/Android
<a rel="nofollow" href="http://blog.danlew.net/2014/11/19/styles
Handling Runtime Changes
<a rel="nofollow" href="http://www.vogella.com/tutorials/Android
Building Apps with Graphics &amp; Animation
<a rel="nofollow" href="http://tools.android.com/tech-docs/new-b
Android項目架構(gòu)
MVP(Model-View-Presenter)模式
<a rel="nofollow" href="http://www.infoq.com/cn/es6-in-depth/"">
《Android源碼設(shè)計模式解析與實戰(zhàn)》一書
Rx在Android中的最佳實踐
函數(shù)調(diào)用時,傳遞參數(shù)應(yīng)該是不可變的(Immutable)
ProGuard
面向?qū)ο罅笤瓌t(SOLID+)
深入理解Java虛擬機
深入Java深淺拷貝、immutable、unmodifiable
Best Practices for User Input
UI上的一些高效方式/最佳實踐
<a rel="nofollow" href="https://blog.stylingandroid.com/ripples-
Best Practices for User Interface
安卓測試驅(qū)動開發(fā)/安卓測試驗證
暗時間:學(xué)會正確思考
技術(shù)筆記
Aspect Oriented Programming(AOP)
Best Practices for Background Jobs
安卓系統(tǒng)動效專題
Feed系統(tǒng)的設(shè)計
Data binding(MVVM,Model-View-ViewModel)
Effective Java一書筆記
<a rel="nofollow" href="http://developer.android.com/training/in
Rx (Reactive eXtention)
MultiDex專題
一些很棒的點子
WebRTC

安卓性能優(yōu)化

性能優(yōu)化的幾大考慮

  • Mobile Context
    • 資源受限
      • 內(nèi)存,普遍較小,512MB很常見,開發(fā)者的機器一般比用戶的機器高端
      • CPU,核心少,運算能力沒有全開
      • GPU,上傳大的紋理(texture),overdraw
    • 內(nèi)存開銷大,會導(dǎo)致系統(tǒng)換入換出更頻繁,GC更頻繁,APP被kill、被重啟更頻繁,不僅會消耗更多電量,而且GC會消耗大量時間,使得應(yīng)用程序渲染速度低于60fps(GC耗時dalvik 10-20ms,ART 2-3ms)
    • 外部存儲與網(wǎng)絡(luò),也是受限的,需要考慮資源的使用、網(wǎng)絡(luò)請求的優(yōu)化
  • The Rules: Memory
    • Avoid Allocations in Inner Loops
    • Avoid Allocations When Possible
      • Cached objects
      • Object pools:注意線程安全問題
      • ArrayList v.s. 數(shù)組
      • 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語句),會導(dǎo)致一個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線程的,如果有動畫的時候進行耗時操作,很可能導(dǎo)致動畫不流暢,耗時操作包括:
      • Layout:當動畫正在播放的時候,要避免改變View(延遲改變);同時選擇動畫也需要避免會觸發(fā)layout的動畫,例如translationX,translationY只會導(dǎo)致延遲的layout操作,而LayoutParams屬性,則會導(dǎo)致即時的layout。
      • Inflation:動畫過程中避免inflate新的view,比如啟動新的activity,或者ListView滑動到不同type的區(qū)域。
    • 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.
      • 結(jié)合的準則就是根據(jù)他們是否需要單獨和用戶完成交互(響應(yīng)點擊事件等)
    • Avoid RelativeLayout Near the Top of the View Hierarchy RelativeLayout需要兩次measurement passes才能確定布局正確,嵌套RelativeLayout,是冪乘關(guān)系
    • 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:相關(guān)數(shù)據(jù)一個請求分發(fā)完畢;不相關(guān)的數(shù)據(jù)分接口分發(fā);客戶端應(yīng)對獲取的數(shù)據(jù)具備足夠的信息;
  • The Rules: Language and Libraries
    • Use Android-Appropriate Data Structures: ArrayMap, SparseArray
    • Serialization
      • Parcelable:安卓系統(tǒng)IPC格式;把Parcel寫到磁盤是不安全的;解包方必須能訪問Parcel的類,否則將失?。惶囟ǖ念悾˙itmap,CursorWindow)將被寫到SharedPreference中,而通過Parcel傳遞的只是文件的fd,存在性能優(yōu)化的空間,但是也節(jié)約了內(nèi)存;
      • Persistable Bundles:API 21引入,序列化為XML,支持的類型比Parcel少,但是為Bundle子類,某些場景方便處理;
      • Avoid Java Serialization:額外開銷更大,性能更差
      • XML and JSON:效率更低,復(fù)雜數(shù)據(jù)應(yīng)考慮前述選項
    • Avoid JNI
      • 需要考慮多種處理器架構(gòu),指針用long保存
      • java->jni, jni->java調(diào)用開銷都很大,一次JNI調(diào)用做盡可能多的工作
      • 內(nèi)存管理,java對象管理jni對應(yīng)對象的生命周期
      • 錯誤處理,在調(diào)用JNI之前檢查參數(shù)
      • 參數(shù)對象盡量“傳值”調(diào)用,即:展開后傳遞,不要在JNI里面使用指針訪問成員,避免JNI過程中對象被回收
    • Prefer Primitive Types:內(nèi)存、性能
  • 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開啟報錯,命令行開啟也報錯,是手機系統(tǒng)的原因?
      • AllocationTracker
        • AS集成,點擊按鈕,對APP進行操作,再點擊按鈕結(jié)束,capture將在AS中打開,查看哪里分配了內(nèi)存,排查內(nèi)存分配性能問題利器
      • Traceview
        • 根據(jù)Traceview的結(jié)果,查看耗時排名靠前的方法,分析原因,提高性能
      • Hierarchyviewer
        • 查看當前view hierarchy中每個view的繪制實踐,繪制較慢的該工具會給出提示(不同顏色)
      • MAT (Memory Analysis Tool)
        • 先通過heap dump把堆快照導(dǎo)出,再通過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繪制的結(jié)果緩存起來,后續(xù)的更新只需要重新渲染即可,省去了View的繪制,以及繪制指令的傳輸部分,但是硬件加速一開始的時候有額外的初始化工作(緩存)
        • 如果View特別簡單,僅僅是一個單顏色區(qū)域,那硬件加速的額外開銷可能得不償失
        • 如果View在動畫過程中不斷invalidate,或者其內(nèi)容不斷變化,硬件加速的效果將大打折扣
        • 如果動畫發(fā)生在ViewGroup上,而其子View相對于ViewGroup也是在發(fā)生變化時,就不應(yīng)該把硬件加速設(shè)置在ViewGroup上,因為動畫過程中ViewGroup的內(nèi)容是不斷變化的(子View也在不斷變化),而是應(yīng)該把加速設(shè)置在各個子View上
        • GPU存儲空間有限,僅當有必要時才使用硬件加速
        • profile GPU rendering和show hardware layers updates是很好的效果評估工具

谷歌安卓團隊對于性能優(yōu)化的建議

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

    • DisplayList:包含需要GPU繪制到屏幕上的數(shù)據(jù)信息
    • 在某個View第一次被渲染時,會創(chuàng)建DisplayList,當這個View要顯示到屏幕上時,會執(zhí)行GPU的繪制指令來進行渲染。如果后續(xù)有移動這個View的位置等操作而需要再次渲染這個View時,只需要額外操作一次渲染指令即可。如果修改了View中的某些可見組件,將需要進行創(chuàng)建DisplayList、執(zhí)行渲染指令整個過程。
    • 上述步驟可簡化為下圖,需要的時間取決于View的復(fù)雜度,View重繪引起的View hierarchy的變化的復(fù)雜度

    http://wiki.jikexueyuan.com/project/notes/images/layout_three_steps.png" alt="layout_three_steps.png" />

    • 可用工具:Profile GPU Rendering查看渲染的表現(xiàn)性能;Show GPU view updates查看視圖更新的操作;HierarchyViewer查看界面布局結(jié)構(gòu);
    • 目標/要點:使布局扁平化,移除非必要組件,避免使用嵌套layout(尤其是根節(jié)點方向的RelativeLayout和有weight的LinearLayout)
  • S1E7: Overdraw, Cliprect, QuickReject
    • 標準組件(或其組合)的Overdraw可以使用工具來檢測、消除;同時系統(tǒng)也會避免繪制完全不可見的組件;
    • 完全自定義組件(重寫onDraw方法),上述方案將無法實現(xiàn);可通過canvas.clipRect()來幫助系統(tǒng)識別可見區(qū)域,完全在矩形外面的內(nèi)容(組件)將不會繪制、渲染,但有部分在矩形內(nèi)的仍會繪制、渲染;
    • canvas.quickreject()函數(shù)可以判斷是否和某個矩形相交,如果不相交,則可以直接跳過;
    • 性能優(yōu)化總原則之一:先測量效果,發(fā)現(xiàn)問題再尋找根源,嘗試改進后要再次測量效果進行對比
  • S1E8: Memory Churn and performance

    • Android的堆內(nèi)存分代回收模型如下圖示

    http://wiki.jikexueyuan.com/project/notes/images/memory_mode_generation.png" alt="memory_mode_generation.png" />

    • GC時會暫停所有其他線程,導(dǎo)致GC頻繁的可能原因
      • Memory Churn(內(nèi)存抖動),大量對象被創(chuàng)建,然后立即被銷毀,例如在onDraw等函數(shù)中創(chuàng)建對象
      • 瞬間產(chǎn)生大量的對象會嚴重占用Young Generation的內(nèi)存區(qū)域,當達到閥值,剩余空間不夠的時候,也會觸發(fā)GC。同時可能觸發(fā)其他區(qū)域(代)的GC
    • 使用Android studio的Memory Monitor,可以觀察是否存在內(nèi)存抖動,Memory monitor集成了Allocation Tracker,使用Allocation Tracker可以查看每個線程的內(nèi)存分配情況,可以具體到某個函數(shù)的某行代碼
    • 平常需要注意的是:onDraw等這樣會被高頻率反復(fù)調(diào)用的函數(shù)、循環(huán)體內(nèi)部等,避免創(chuàng)建新對象
  • S1E9: Garbage Collection in Android
    • 安卓系統(tǒng)使用的虛擬機是三代模型:Young, old, permanent;越往后每代GC時間越長;
    • 應(yīng)該避免頻繁GC
  • S1E10: Performance Cost of Memory Leaks
    • 一個典型的Activity泄露場景:Activity類內(nèi)部的非靜態(tài)Handler子類(或匿名類)實例
    • LeakCanary工具是檢測內(nèi)存泄漏的好工具
  • S1E11: Memory Performance
    • 工具集:Memory Monitor:觀察是否存在內(nèi)存抖動;Allocation Tracker:追蹤內(nèi)存分配情況;Heap Tool:查看內(nèi)存快照,分析可能泄露的對象;
  • S1E12: Tool - Memory Monitor,無甚可記
  • S1E13: Battery Performance
    • 盡量減少喚醒屏幕的次數(shù)與持續(xù)的時間,使用WakeLock來處理喚醒的問題,能夠正確執(zhí)行喚醒操作并根據(jù)設(shè)定及時關(guān)閉操作進入睡眠狀態(tài)。
    • 某些非必須馬上執(zhí)行的操作,例如上傳歌曲,圖片處理等,可以等到設(shè)備處于充電狀態(tài)或者電量充足的時候才進行。
    • 觸發(fā)網(wǎng)絡(luò)請求的操作,每次都會保持無線信號持續(xù)一段時間,我們可以把零散的網(wǎng)絡(luò)請求打包進行一次操作,避免過多的無線信號引起的電量消耗。
    • Battery Historian Tool:查看APP電量消耗情況
    • JobScheduler API:對任務(wù)進行定時處理,例如等到手機處于充電狀態(tài),或連接到WiFi時處理任務(wù)
    • JobScheduler在API 21引入,Google play service有backport: GcmNetworkManager,也有第三方backport
  • S1E14: Understanding Battery Drain on Android
    • 使用WakeLock或者JobScheduler喚醒設(shè)備處理定時的任務(wù)之后,一定要及時讓設(shè)備回到初始狀態(tài)。
    • 每次喚醒無線信號進行數(shù)據(jù)傳遞,都會消耗很多電量,它比WiFi等操作更加的耗電
  • S1E15: Battery Drain and WakeLocks

    • WakeLock使用非精準定時器,允許系統(tǒng)為不同應(yīng)用的wake lock請求進行打包處理,節(jié)約電量消耗
    • JobScheduler API還能做更多的事情,例如等到充電,或者連接上wifi時處理任務(wù)
  • S2E1: Battery Drain and Networking
    • 捆綁網(wǎng)絡(luò)請求,按批次執(zhí)行,可降低激活網(wǎng)絡(luò)、等待休眠過程的均攤成本;使用JobScheduler可以實現(xiàn)更多的優(yōu)化策略;
    • 預(yù)取與壓縮
    • 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平臺提供的既有運動數(shù)據(jù),而不是自己去實現(xiàn)監(jiān)聽采集數(shù)據(jù),因為大多數(shù)Android Watch自身記錄Sensor數(shù)據(jù)的行為是有經(jīng)過做電量優(yōu)化的。
    • 其次在Activity不需要監(jiān)聽某些Sensor數(shù)據(jù)的時候需要盡快釋放監(jiān)聽注冊。
    • 還有我們需要盡量控制更新的頻率,僅僅在需要刷新顯示數(shù)據(jù)的時候才觸發(fā)獲取最新數(shù)據(jù)的操作。
    • 另外我們可以針對Sensor的數(shù)據(jù)做批量處理,待數(shù)據(jù)累積一定次數(shù)或者某個程度的時候才更新到UI上。
    • 最后當Watch與Phone連接起來的時候,可以把某些復(fù)雜操作的事情交給Phone來執(zhí)行,Watch只需要等待返回的結(jié)果。
  • S2E3: Smooth Android Wear Animation
    • 動畫優(yōu)化例1:在一個圓形的鐘表圖上,我們把時鐘的指針摳出來當做單獨的圖片進行旋轉(zhuǎn)會比旋轉(zhuǎn)一張完整的圓形圖的所形成的幀率要高56%
    • 動畫優(yōu)化例2:把背景圖片單獨拎出來設(shè)置為一個獨立的View,通過setLayerType()方法使得這個View強制用Hardware來進行渲染
    • 動畫優(yōu)化例3:使用PropertyAnimation或者ViewAnimation來操作實現(xiàn),Android系統(tǒng)會自動對這些Animation做一定的優(yōu)化處理
  • S2E4: Android Wear Data Batching
    • 僅僅在真正需要刷新界面的時候才發(fā)出請求
    • 盡量把計算復(fù)雜操作的任務(wù)交給Phone來處理
    • Phone僅僅在數(shù)據(jù)發(fā)生變化的時候才通知到Wear
    • 把零碎的數(shù)據(jù)請求捆綁一起再進行操作
  • S2E5: Object Pools
    • 避免對象的頻繁創(chuàng)建與銷毀,減少內(nèi)存抖動;避免重量級對象的創(chuàng)建與銷毀,降低創(chuàng)建開銷;
    • lazy分配 v.s. 預(yù)分配
    • 慎用,需要手動釋放,謹防內(nèi)存泄漏;為了確保所有的對象能夠正確被釋放,需要保證加入對象池的對象和其他外部對象沒有互相引用的關(guān)系。
  • S2E6: To Index or Iterate?

    • for index
    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
    • 安卓系統(tǒng)提供了LruCache類,已實現(xiàn)LRU算法
    • 慎用,手動釋放緩存的對象,謹防內(nèi)存泄漏
  • S2E8: Using LINT for Performance Tips,無甚可記
  • S2E9: Hidden Cost of Transparency
    • 通常來說,對于不透明的View,顯示它只需要渲染一次即可,可是如果這個View設(shè)置了alpha值,會至少需要渲染兩次
    • 在某些情況下,一個包含alpha的View有可能會觸發(fā)該View在HierarchyView上的父View都被額外重繪一次
    • 在動畫開始時,setLayerType(View.LAYER_TYPE_HARDWARE, null),動畫結(jié)束后,setLayerType(View.LAYER_TYPE_NONE, null);在API >= 16時,可以只調(diào)用ViewPropertyAnimator.alpha(0.0f).withLayer()接口即可;
    • 使用shadow時,重寫View的hasOverlappingRendering()接口,返回false;
    • 只有當確定瓶頸是這部分view的渲染時,才有必要這樣優(yōu)化;
  • S2E10: Avoiding Allocations in onDraw(),記住,實踐即可
  • S2E11: Tool: Strict Mode
    • 開發(fā)者選項Strict Mode,如果存在潛在ANR隱患(主線程磁盤IO,網(wǎng)絡(luò)請求等),屏幕會閃現(xiàn)紅色
    • 也有代碼的API,StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()...build());StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()...build());,如果有隱患,屏幕會閃爍,并且有l(wèi)og輸出
  • S2E12: Custom Views and Performance,避免以下問題:
    • Useless calls to onDraw(),僅在View內(nèi)容發(fā)生變化時才調(diào)用View.invalidate();盡量使用ClipRect等方法來提高繪制的性能;
    • Useless pixels,減少繪制時不必要的元素,對于那些不可見的元素不要繪制;
    • Wasted CPU cycles,不在屏幕上的元素,可以使用Canvas.quickReject把他們給剔除;另外盡量使用GPU來進行UI的渲染,這樣能夠極大的提高程序的整體表現(xiàn)性能;
  • S2E13: Batching Background Work Until Later,上文有涉及
  • S2E14: Smaller Pixel Formats
    • dalvik虛擬機不會自動進行內(nèi)存整理
    • Android為圖片提供了4種解碼格式:ARGB_8888, RGB_565, ARGB_4444, ALPHA_8,但是實際測試的時候,如果圖片不變,顯示圖片的View區(qū)域不變,也不限制Bitmap只有View那么大,單純設(shè)置bitmapOption.inPreferredConfig好像沒什么用
    • 可以通過將解碼的Bitmap大小設(shè)置為View大小的兩倍(單位是dp),實測內(nèi)存基本可以降到1/16(當然前提是圖片比View大很多),而且視覺效果基本一樣
  • S2E15: Smaller PNG Files,以下建議結(jié)合微信安卓團隊的分享
    • 沒有透明度的文件,都可以用jpg
    • 對于體積特別大(超過50k)的圖片資源可以考慮有損壓縮,jpg采用優(yōu)圖壓縮,png嘗試采用pngquant壓縮,輸出視覺判斷是否可行;
    • 對assets中的圖片資源也使用aapt的crunch做圖片預(yù)處理;
    • crunch有可能會使圖片變大,在這種情況,我們可以替換成原圖。需要注意的是對于.9.png,由于crunch過程中去除了黑邊,所以不能替換;
    • 對于沒有透明區(qū)域的png圖片,可以轉(zhuǎn)成jpg格式。
    • Webp:既保留png格式的優(yōu)點,又能夠減少圖片大小
  • S2E16: Pre-scaling Bitmaps
    • bitmapOption.inSampleSize屬性,可等比例縮放圖片,而且不會加載原圖到內(nèi)存
    • inScaled,inDensity,inTargetDensity的屬性來對解碼圖片做處理
    • inJustDecodeBounds,只會先讀取圖片尺寸,不會加載到內(nèi)存
  • S2E17: Re-using Bitmaps
    • 設(shè)置了inbitmapOption.Bitmap后,系統(tǒng)會嘗試使用已有的Bitmap來保存新的圖片,避免內(nèi)存的反復(fù)創(chuàng)建,但是這一方法有些不足:
      • 在SDK 11 -> 18之間,重用的bitmap大小必須是一致的
      • 從SDK 19開始,新申請的bitmap大小必須小于或者等于已經(jīng)賦值過的bitmap大小
      • 新申請的bitmap與舊的bitmap必須有相同的解碼格式
    • 可以結(jié)合對象池,給不同解碼格式,不同大小的圖片準備不同的Bitmap并進行復(fù)用,不過這樣做難度較大
    • 開源圖片加載庫Glide,Fresco
  • S2E18: The Performance Lifecycle
    • Gather:測試收集數(shù)據(jù)
    • Insight:分析數(shù)據(jù)
    • Action:解決問題
    • 再次進行測試,驗證效果
  • S2E19: Tools not Rules,上述建議需要靈活應(yīng)用
  • S2E20: Memory Profiling 101,上文已述

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

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分配:會有一個默認初始值,以后空間不夠時按倍增策略進行擴展
      • 如果創(chuàng)建時就知道其大小,則可以new一個已知容量的ArrayList,避免后面擴容、數(shù)據(jù)復(fù)制的成本
    • StringBuilder:同樣的,也可以先給一個預(yù)估的大小,然后直接初始化該大小的StringBuilder;安卓開發(fā)build時會自動把String的拼接操作轉(zhuǎn)化為StringBuilder實現(xiàn),然而這種自動的轉(zhuǎn)換未必高效;
      • 例子
        for (int x = 0; x < valueCount; x++) {
            cleanFiles[x] = new File(directory, key + "." + x);
            dirtyFiles[x] = new File(directory, key + "." + x + ".tmp");
        }

        ===>>>

        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);
        }
    • 其他
      • 對函數(shù)的調(diào)用(尤其是虛函數(shù)、接口函數(shù))結(jié)果,如果同一個作用域中有多次調(diào)用,且結(jié)果確定不變,應(yīng)該將他們轉(zhuǎn)化為一次調(diào)用:for (int i = 0, size = list.size(); i < size; i++)
      • 對集合的遍歷,不要使用語法糖,會有額外開銷(Iterator創(chuàng)建、虛函數(shù)調(diào)用等)

NimbleDroid的建議

  • 性能優(yōu)化的流程 http://wiki.jikexueyuan.com/project/notes/images/perf_tune_process.png" alt="perf_tune_process.png" />
  • 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
上一篇:WebRTC下一篇:深入理解Java虛擬機