編輯:關於android開發
本章介紹android高級開發中,對於性能方面的處理。主要包括電量,視圖,內存三個性能方面的知識點。
Overdraw就是過度繪制,是指在一幀的時間內(16.67ms)像素被繪制了多次,理論上一個像素每次只繪制一次是最優的,但是由於重疊的布 局導致一些像素會被多次繪制,而每次繪制都會對應到CPU的一組繪圖命令和GPU的一些操作,當這個操作耗時超過16.67ms時,就會出現掉幀現象,表現為應用卡頓,所以對重疊不可見元素的重復繪制會產生額外的開銷,需要盡量減少Overdraw的發生。
Android提供了測量Overdraw的選項,在開發者選項-調試GPU過度繪制(Show GPU Overdraw),打開選項就可以看到當前頁面Overdraw的狀態,就可以觀察屏幕的繪制狀態。該工具會使用三種不同的顏色繪制屏幕,來指示 overdraw發生在哪裡以及程度如何,其中:
提高程序在視圖方面的性能, 總的原則就是:盡量避免重疊不可見元素的繪制。
Android提供的Layout控件主要包括 LinearLayout、TableLayout、FrameLayout、RelativeLayout。同一個界面可以使用不同的容器控件來表達,但是各個容器控件描述界面的復雜度是不一樣的。一般來LinearLayout最易,RelativeLayout較復雜。 LinearLayout只能用來描述一個方向上連續排列的控件,而RelativeLayout幾乎可以用於描述任意復雜度的界面。。綜上所述: LinearLayout易用,效率高,表達能力有限。RelativeLayout復雜,表達能力強,效率低。從減少overdraw的角度來看,LinearLayout會增加控件數的層級,自然是RelativeLayout 更優,但是當某一界面在使用LinearLayout並不會比RelativeLayout帶來更多的控件數和控件層級時,LinearLayout則是首選。
當使用Android自帶的一些主題時,window會被默認添加一個純色的背景,這個背景是被DecorView持有的。當自定義布局時又添加了一張背景圖或者設置背景色,那麼DecorView的background此時是無用的,但是它會產生一次Overdraw,帶來繪制性能損耗。去掉window的背景可以在onCreate()中setContentView()之後調用 getWindow().setBackgroundDrawable(null);或者在theme中添加 android:windowbackground="null"。
父容器若已經有了背景,可不設置對應子控件的背景,及大的布局背景已經設置,應避免設置局部重復的背景。
對於自定義的view視圖,可以通過canvas.clipRect()來幫助系統識別那些可見的區域。這個方法可以指定一塊矩形區域,只有在這個區域內才會被繪制,其他的區域會被忽視。這個API可以很好的幫助那些有多組重疊組件的自定義View來控制顯示的區域。同時clipRect方法還可以幫助節約CPU與GPU資源,在clipRect區域之外的繪制指令都不會被執行,那些部分內容在矩形區域內的組件,仍然會得到繪制。除了clipRect方法之外,我們還可以使用canvas.quickreject()來判斷是否沒和某個矩形相交,從而跳過那些非矩形區域內的繪制操作。
當遇到這樣的情況,運行時動態根據條件來決定顯示哪個View或布局。常用的做法是把View都寫在上面,先把它們的可見性都設為 View.GONE,然後在代碼中動態的更改它的可見性。這種模式的缺點是耗費資源。雖然把View 的初始View.GONE但是在Inflate布局的時候View仍然會被Inflate,程序運行時仍然會創建對象,會被實例化,會被設置屬性,會耗費內存等資源。
推薦的做法是使用android.view.ViewStub,ViewStub是一個輕量級的View,它一個看不見的,不占布局位置,占用資源非常小的控件。可以為ViewStub指定一個布局,在Inflate布局的時候,只有ViewStub會被初始化,然後當ViewStub被設置為可見的時候,或是調用了ViewStub.inflate()的時候,ViewStub所向的布局就會被Inflate和實例化,然後ViewStub的布局屬性都會傳給它所指向的布局。這樣,就可以使用ViewStub來方便的在運行時,要還是不要顯示某個布局。
給ImageView加一個邊框,通常在ImageView後面設置一張背景圖,露出邊框便完美解決問題,此時這個 ImageView,設置了兩層drawable,兩層drawable的重疊區域去繪制了兩次,導致 overdraw。優化方案: 將背景drawable制作成draw9patch,並且將和前景重疊的部分設置為透明。由於Android的2D渲染器會優化draw9patch中的透明區域,從而優化了這次overdraw。
使用Merge標簽來做容器控件。第一種子視圖不需要指定任何針對父視圖的布局屬性,就是說父容器僅僅是個容器,子視圖只需要直接添加到父視圖上用於顯示 就 行。另外一種是假如需要在LinearLayout裡面嵌入一個布局 (或者視圖),而恰恰這個布局(或者視圖)的根節點也是LinearLayout,這樣就多了一層沒有用的嵌套,無疑這樣只會拖慢程序速度。而這個時候如 果我們使用merge根標簽就可以避免那樣的問題。
每一個進程的Dalvik Heap都反映了使用內存的占用范圍。這就是通常邏輯意義上提到的Dalvik Heap Size,它可以隨著需要進行增長,但是增長行為會有一個系統為它設定上限。
邏輯上講的Heap Size和實際物理意義上使用的內存大小是不對等的,Proportional Set Size(PSS)記錄了應用程序自身占用以及與其他進程進行共享的內存。
Android 系統並不會對Heap中空閒內存區域做碎片整理。系統僅僅會在新的內存分配之前判斷Heap的尾端剩余空間是否足夠,如果空間不夠會觸發GC操作,從而騰 出更多空閒的內存空間。在Android的高級系統版本裡面針對Heap空間有一個Generational Heap Memory的模型,最近分配的對象會存放在Young Generation區域。當這個對象在該區域停留的時間達到一定程度,它會被移動到Old Generation,最後累積一定時間再移動到Permanent Generation區域。系統會根據內存中不同的內存數據類型分別執行不同的GC操作。例如,剛分配到Young Generation區域的對象通常更容易被銷毀回收,同時在Young Generation區域的GC操作速度會比Old Generation區域的GC操作速度更快(如圖1所示)。
圖1 根據不同內存數據類型執行不同GC操作
每一個Generation的內存區域都有固定的大小。隨著新的對象陸續被分配到此區域,當對象總的大小臨近這一級別內存區域的閥值時,會觸發GC操作,以便騰出空間來存放其他新的對象(如圖2所示)。
圖2 對象值臨近閥值觸發GC操作
通常情況下,GC發生的時候,所有的線程都是會被暫停的。執行GC所占用的時間和它發生在哪一個Generation也有關系,Young Generation中的每次GC操作時間是最短的,Old Generation其次,Permanent Generation最長。執行時間的長短也和當前Generation中的對象數量有關,遍歷樹結構查找20000個對象比起遍歷50個對象自然是要慢 很多的。
LeakCanary是一個用於檢測內存洩漏的工具,可以用於Java和Android,是由著名開源組織Square貢獻。
Android設備根據硬件與軟件的設置差異而存在不同大小的內存空間,他們為應用程序設置了不同大小的Heap限制阈值。設計時可以通過調用getMemoryClass()來獲取應用的可用Heap大小。在一些特殊的情景下,你可以通過在manifest的application標簽下添加 largeHeap=true的屬性來為應用聲明一個更大的heap空間。然後,你可以通過getLargeMemoryClass()來獲取到這個更大的heap size阈值。
然而,聲明得到更大Heap阈值的本意是為了一小部分會消耗大量RAM的應用(例如一個大圖片的編輯應用)。不要輕易的因為你需要使用更多的內存而去請求一個大的Heap Size。只有當你清楚的知道哪裡會使用大量的內存並且知道為什麼這些內存必須被保留時才去使用large heap。因此請謹慎使用large heap屬性。使用額外的內存空間會影響系統整體的用戶體驗,並且會使得每次gc的運行時間更長。
例如,在設計ListView或者GridView的Bitmap LRU緩存的時候,需要考慮的點有:
hdpi/xhdpi/xxhdpi等等不同dpi的文件夾下的圖片在不同的設備上會經過scale的處理。例如我們只在hdpi的目錄下放置了一 張100100的圖片,那麼根據換算關系,xxhdpi的手機去引用那張圖片就會被拉伸到200200。需要注意到在這種情況下,內存占用是會顯著提高 的。對於不希望被拉伸的圖片,需要放到assets或者nodpi的目錄下。
在某些情況下,我們需要事先評估那些可能發生OOM的代碼,對於這些可能發生OOM的代碼,加入catch機制,可以考慮在catch裡面嘗試一次降級的內存分配操作。例如decode bitmap的時候,catch到OOM,可以嘗試把采樣比例再增加一倍之後,再次嘗試decode。
因為static的生命周期過長,和應用的進程保持一致,使用不當很可能導致對象洩漏,在Android中應該謹慎使用static對象。
雖然單例模式簡單實用,提供了很多便利性,但是因為單例的生命周期和應用保持一致,使用不合理很容易出現持有對象的洩漏。
應用需要在後台使用service,除非它被觸發並執行一個任務,否則其他時候Service都應該是停止狀態。另外需要注意當這個service 完成任務之後因為停止service失敗而引起的內存洩漏。 當你啟動一個Service,系統會傾向為了保留這個Service而一直保留Service所在的進程。這使得進程的運行代價很高,因為系統沒有辦法把 Service所占用的RAM空間騰出來讓給其他組件,另外Service還不能被Paged out。這減少了系統能夠存放到LRU緩存當中的進程數量,它會影響應用之間的切換效率,甚至會導致系統內存使用不穩定,從而無法繼續保持住所有目前正在運行的service。 建議使用IntentService,它會在處理完交代給它的任務之後盡快結束自己。
ProGuard能夠通過移除不需要的代碼,重命名類,域與方法等等對代碼進行壓縮,優化與混淆。使用ProGuard可以使得你的代碼更加緊湊,這樣能夠減少mapping代碼所需要的內存空間。
考慮使用ArrayMap/SparseArray而不是HashMap等傳統數據結構。
HashMap的容器,相比起 Android專門為移動操作系統編寫的ArrayMap容器,在大多數情況下,都顯示效率低下,更占內存。通常的HashMap的實現方式更加消耗內存,因為它需要一個額外的實例對象來記錄Mapping操作。另外,SparseArray更加高效,在於他們避免了對key與value的自動裝箱 (autoboxing),並且避免了裝箱後的解箱。
Android 官方培訓課程提到過“Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.”,具體原理請參考《Android性能優化典范(三)》,所以請避免在Android裡面使用到枚舉。
Bitmap是一個極容易消耗內存的大胖子,減小創建出來的Bitmap的內存占用可謂是重中之重,通常來說有以下2個措施:
在涉及給到資源圖片時,我們需要特別留意這張圖片是否存在可以壓縮的空間,是否可以使用更小的圖片。盡量使用更小的圖片不僅可以減少內存的使用,還能避免出現大量的InflationException。假設有一張很大的圖片被XML文件直接引用,很有可能在初始化視圖時會因為內存不足而發生InflationException,這個問題的根本原因其實是發生了OOM。
大多數對象的復用,最終實施的方案都是利用對象池技術,要麼是在編寫代碼時顯式地在程序裡創建對象池,然後處理好復用的實現邏輯。要麼就是利用系統框架既有的某些復用特性,減少對象的重復創建,從而降低內存的分配與回收。
Android 系統本身內置了很多的資源,比如字符串、顏色、圖片、動畫、樣式以及簡單布局等,這些資源都可以在應用程序中直接引用。這樣做不僅能減少應用程序的自身負重,減小APK的大小,還可以在一定程度上減少內存的開銷,復用性更好。但是也有必要留意Android系統的版本差異性,對那些不同系統版本上表現存在很大差異、不符合需求的情況,還是需要應用程序自身內置進去。
在程序中我們經常會進行查詢數據庫的操作,但時常會存在不小心使用Cursor之後沒有及時關閉的情況。這些Cursor的洩露,反復多次出現的話會對內存管理產生很大的負面影響,我們需要謹記對Cursor對象的及時關閉。
16)避免在onDraw方法裡面執行對象的創建
類似onDraw等頻繁調用的方法,一定需要注意避免在這裡做創建對象的操作,因為他會迅速增加內存的使用,而且很容易引起頻繁的gc,甚至是內存抖動。
17)StringBuilder
在有些時候,代碼中會需要使用到大量的字符串拼接的操作,這種時候有必要考慮使用StringBuilder來替代頻繁的“+”。
通常來說,Activity的洩漏是內存洩漏裡面最嚴重的問題,它占用的內存多,影響面廣,需要特別注意以下兩種情況導致的Activity洩漏:
最典型的場景是Handler導致的Activity洩漏,如果Handler中有延遲的任務或者是等待執行的任務隊列過長,都有可能因為Handler繼 續執行而導致Activity發生洩漏。此時的引用關系鏈是Looper -> MessageQueue -> Message -> Handler -> Activity。為了解決這個問題,可以在UI退出之前,執行remove Handler消息隊列中的消息與runnable對象。或者是使用Static + WeakReference的方式來達到斷開Handler與Activity之間存在引用關系的目的。
內部類引起的洩漏不僅僅會發生在Activity上,其他任何內部類出現的地方,都需要特別留意!可以考慮盡量使用static類型的內部類,同時使用WeakReference的機制來避免因為互相引用而出現的洩露。
對於大部分非必須使用Activity Context的情況(Dialog的Context就必須是Activity Context),都可以考慮使用Application Context而不是Activity的Context,這樣可以避免不經意的Activity洩露。
電量其實是目前手持設備最寶貴的資源之一,大多數設備都需要不斷的充電來維持繼續使用。不幸的是,對於開發者來說,電量優化是他們最後才會考慮的的事情。但是可以確定的是,千萬不能讓你的應用成為消耗電量的大戶。
有下面一些措施能夠顯著減少電量的消耗:
【原創+譯文】官方文檔中聲明的如何創建抽屜導航欄(Navigation Drawer),navigationdrawer如需轉載請注明出處:http://www.cnbl
今天我用自己寫的一個Demo 和大家詳細介紹一個Android中的對話框的使用技巧。 &
[android] 手機衛士手機實現短信指令獲取位置,android衛士獲取位置 新建一個service的包 新建一個GPSService類繼承系統的Serv
搭建流媒體服務器需求:現在需要搭建一台流媒體服務器,為了將主講人的電腦桌面屏幕和聲音直播給遠端的人接收實時觀看,要求延遲在5秒左右。理論上RTSP、RTMP、HTTP都可
Android studio使用gradle動態構建APP(不同的包,不