以下內(nèi)容整理自互聯(lián)網(wǎng)炼列,僅用于個人學習
1. 合理管理內(nèi)存
1.1 節(jié)制的使用Service
如果應(yīng)用程序需要使用Service來執(zhí)行后臺任務(wù)的話筷频,只有當任務(wù)正在執(zhí)行的時候才應(yīng)該讓Service運行起來骚腥。當啟動一個Service時懊悯,系統(tǒng)會傾向于將這個Service所依賴的進程進行保留,系統(tǒng)可以在LRUcache當中緩存的進程數(shù)量也會減少区匣,導致切換程序的時候耗費更多性能偷拔。我們可以使用IntentService,當后臺任務(wù)執(zhí)行結(jié)束后會自動停止亏钩,避免了Service的內(nèi)存泄漏莲绰。
1.2 當視圖變?yōu)殡[藏狀態(tài)后釋放內(nèi)存
當用戶跳轉(zhuǎn)到不同的應(yīng)用并且你的視圖不再顯示時,你應(yīng)該釋放應(yīng)用視圖所占的資源姑丑。 這時釋放所占用的資源能顯著的提高系統(tǒng)的緩存處理容量蛤签,并且對用戶的體驗質(zhì)量有直接的影響。
重寫Activity的onTrimMemory()方法彻坛,然后在這個方法中監(jiān)聽TRIM_MEMORY_UI_HIDDEN這個級別顷啼,一旦觸發(fā)說明用戶離開了程序踏枣,此時就可以進行資源釋放操作了昌屉。
注意只有當你應(yīng)用的所有視圖元素變?yōu)殡[藏狀態(tài)時你的應(yīng)用才能收到onTrimMemory()回調(diào)方法的TRIM_MEMORY_UI_HIDDEN。這個和onStop()回調(diào)方法不同茵瀑,該方法只有當Activity的實例變?yōu)殡[藏狀態(tài)间驮,或者有用戶移動到應(yīng)用中的另外的activity才會引發(fā)。所以說你雖然實現(xiàn)了onStop()去釋放activity的資源例如網(wǎng)絡(luò)連接或者未注冊的廣播接收者马昨,但是應(yīng)該直到你收到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)才去釋放視圖資源竞帽,否則不應(yīng)該釋放視圖所占用的資源。這里可以確定的是如果用戶通過后退鍵從另外的activity進入到你的應(yīng)用中鸿捧,視圖資源會一直處于可用的狀態(tài)可以用來快速的恢復activity屹篓。
1.3 當內(nèi)容緊張時釋放內(nèi)存
onTrimMemory()方法還有很多種其他類型的回調(diào),可以在手機內(nèi)存降低的時候及時通知我們匙奴,我們應(yīng)該根據(jù)回調(diào)中傳入的級別來去決定如何釋放應(yīng)用程序的資源堆巧。
//應(yīng)用處于運行狀態(tài)并且不會被殺掉, 設(shè)備使用的內(nèi)存比較低,
//系統(tǒng)級會殺掉一些其它的緩存應(yīng)用.
TRIM_MEMORY_RUNNING_CRITICAL
//應(yīng)用處于運行狀態(tài)并且不會被殺掉, 設(shè)備可以使用的內(nèi)存非常低,
//可以把不用的資源釋放一些提高性能(會直接影響程序的性能)
TRIM_MEMORY_RUNNING_LOW
//應(yīng)用處于運行狀態(tài)但是系統(tǒng)已經(jīng)把大多數(shù)緩存應(yīng)用殺掉了,
//你必須釋放掉不是非常關(guān)鍵的資源, 如果系統(tǒng)不能回收足夠的運行內(nèi)存,
//系統(tǒng)會清除所有緩存應(yīng)用并且會把正在活動的應(yīng)用殺掉.
TRIM_MEMORY_RUNNING_CRITICAL
//還有, 當你的應(yīng)用被系統(tǒng)正緩存時,
//通過 onTrimMemory() 回調(diào)方法可以收到以下幾個內(nèi)存級別:
TRIM_MEMORY_BACKGROUND
//系統(tǒng)處于低內(nèi)存的運行狀態(tài)中并且你的應(yīng)用處于緩存應(yīng)用列表的初級階段.
//雖然你的應(yīng)用不會處于被殺的高風險中, 但是系統(tǒng)已經(jīng)開始清除緩存列表中的其它應(yīng)用,
// 所以你必須釋放資源使你的應(yīng)用繼續(xù)存留在列表中以便用戶再次回到你的應(yīng)用時能快速恢復進行使用.
TRIM_MEMORY_MODERATE
//系統(tǒng)處于低內(nèi)存的運行狀態(tài)中并且你的應(yīng)用處于緩存應(yīng)用列表的中級階段.
// 如果系運行內(nèi)存收到限制, 你的應(yīng)用有被殺掉的風險.
TRIM_MEMORY_COMPLETE
系統(tǒng)處于低內(nèi)存的運行狀態(tài)中如果系統(tǒng)現(xiàn)在沒有內(nèi)存回收你的應(yīng)用將會第一個被殺掉,你必須釋放掉所有非關(guān)鍵的資源從而恢復應(yīng)用的狀態(tài)。
當系統(tǒng)開始清除緩存應(yīng)用列表中的應(yīng)用時谍肤,雖然系統(tǒng)的主要工作機制是自下而上啦租,但是也會通過殺掉消費大內(nèi)存的應(yīng)用從而使系統(tǒng)獲得更多的內(nèi)存,所以在緩存應(yīng)用列表中消耗更少的內(nèi)存將會有更大的機會留存下來以便用戶再次使用時進行快速恢復荒揣。
1.4 檢查可以使用多大的內(nèi)存
前面提到篷角,不同的android設(shè)備系統(tǒng)擁有的運行內(nèi)存各自都不同,從而不同的應(yīng)用堆內(nèi)存的限制大小也不一樣系任。你可以通過調(diào)用ActivityManager中的getMemoryClass()函數(shù)可以通過以兆為單位獲取當前應(yīng)用可用的內(nèi)存大小恳蹲, 如果你想獲取超過最大限度的內(nèi)存則會發(fā)生OutOfMemoryError。
有一個特別的情況俩滥,可以在manifest文件中的<application>標簽中設(shè)置largeHeap屬性的值為true時阱缓,當前應(yīng)用就可以獲取到系統(tǒng)分配的最大堆內(nèi)存。如果你設(shè)置了該值举农,可以通過ActivityManager的getLargeMemoryClass()函數(shù)獲取最大的堆內(nèi)存荆针。
然后,,只有一小部分應(yīng)用需要消耗大量堆內(nèi)存(比如大照片編輯應(yīng)用).颁糟。從來不需要使用大量內(nèi)存僅僅是因為你已經(jīng)消耗了大量的內(nèi)存并且必須快速修復它航背,你必須使用它是因為你恰好知道所有的內(nèi)存已經(jīng)被分配完了而你必須要保留當前應(yīng)用不會被清除掉。甚至當你的應(yīng)用需要消耗大量內(nèi)存時棱貌,你應(yīng)該盡可能的避免這種需求玖媚。使用大量內(nèi)存后,當你切換不同的應(yīng)用或者執(zhí)行其它類似的操作時婚脱,因為長時間的內(nèi)存回收會導致系統(tǒng)的性能下降從而漸漸的會損害整個系統(tǒng)的用戶體驗今魔。
另外,大內(nèi)存不是所有的設(shè)備都相同障贸。當跑在有運行內(nèi)存限制的設(shè)備上時错森,大內(nèi)存和正常的堆內(nèi)存是一樣的。所以如果你需要大內(nèi)存篮洁,你就要調(diào)用getMemoryClass()函數(shù)查看正常的堆內(nèi)存的大小并且盡可能使內(nèi)存使用情況維護在正常堆內(nèi)存之下涩维。
1.5 避免在Bitmap上浪費內(nèi)存
讀取一個Bitmap圖片的時候,不要去加載不需要的分辨率袁波,可以進行壓縮圖片等操作瓦阐。
1.6 使用優(yōu)化后的數(shù)據(jù)集合
利用Android框架優(yōu)化后的數(shù)據(jù)容器,比如SparseArray篷牌,SparseBooleanArray和LongSparseArray睡蟋。傳統(tǒng)的HashMap
在內(nèi)存上的實現(xiàn)十分的低效,因為它需要為map中每一項在內(nèi)存中建立映射關(guān)系枷颊。另外戳杀,SparseArray類非常高效因為它避免系統(tǒng)中需要自動封箱(autobox)的key和有些值叫倍。
注意:只是內(nèi)存上進行優(yōu)化,執(zhí)行效率并沒有優(yōu)化豺瘤。
1.7 知曉內(nèi)存的開支情況
在你設(shè)計應(yīng)用各個階段都要很謹慎的考慮所使用的語言和庫帶來的內(nèi)存上的成本和開銷.通常情況下吆倦,表面上看起來無害的會帶來巨大的開銷,下面在例子說明:
- 當枚舉(enum)成為靜態(tài)常量時超過正常兩倍以上的內(nèi)存開銷坐求,在android中你需要嚴格避免使用枚舉
- java中的每個類(包含匿名內(nèi)部類)大約使用500個字節(jié)
- 每個類實例在運行內(nèi)存(RAM)中占用12到16個字節(jié)
- 在hashmap中放入單項數(shù)據(jù)時, 需要為額外的其它項分配內(nèi)存, 總共占用32個字節(jié)
使用很多的不必要類和對象時蚕泽,增加了分析堆內(nèi)存問題的復雜度。
1.8 謹慎使用抽象編程
通常來說桥嗤,使用簡單的抽象是一種好的編程習慣须妻,因為一定程度上的抽象可以提供代碼的伸縮性和可維護性。
然而抽象會帶來非常顯著的開銷:需要執(zhí)行更多的代碼泛领,需要更長時間和更多的運行內(nèi)存把代碼映射到內(nèi)存中荒吏,所以如果抽象沒有帶來顯著的效果就盡量避免。
1.9 盡量避免使用依賴注入框架
使用依賴注入框架貌似看上去把findViewById()這一類的繁瑣操作去掉了渊鞋,但是這些框架為了要搜尋代碼中的注解绰更,通常都需要經(jīng)歷較長的初始化過程,并且將一些你用不到的對象也一并加載到內(nèi)存中锡宋。這些用不到的對象會一直站用著內(nèi)存空間儡湾,可能很久之后才會得到釋放,所以可能多敲幾行代碼是更好的選擇执俩。
1.10 使用多個進程
謹慎使用徐钠,多數(shù)應(yīng)用程序不該在多個進程中運行的,一旦使用不當役首,它甚至會增加額外的內(nèi)存而不是幫我們節(jié)省內(nèi)存尝丐。這個技巧比較適用于哪些需要在后臺去完成一項獨立的任務(wù),和前臺是完全可以區(qū)分開的場景衡奥。比如音樂播放爹袁,關(guān)閉軟件,已經(jīng)完全由Service來控制音樂播放了杰赛,系統(tǒng)仍然會將許多UI方面的內(nèi)存進行保留呢簸。在這種場景下就非常適合使用兩個進程,一個用于UI展示乏屯,另一個用于在后臺持續(xù)的播放音樂。關(guān)于實現(xiàn)多進程瘦赫,只需要在Manifast文件的應(yīng)用程序組件聲明一個android:process屬性就可以了辰晕。進程名可以自定義,但是之前要加個冒號确虱,表示該進程是一個當前應(yīng)用程序的私有進程含友。
2. 高性能編碼優(yōu)化
都是一些微優(yōu)化,在性能方面看不出有什么顯著的提升的。使用合適的算法和數(shù)據(jù)結(jié)構(gòu)是優(yōu)化程序性能的最主要手段窘问。
2.1 避免創(chuàng)建不必要的對象
- 如果有需要拼接的字符串辆童,那么可以優(yōu)先考慮使用StringBuffer或者StringBuilder來進行拼接,而不是加號連接符惠赫,因為使用加號連接符會創(chuàng)建多余的對象把鉴,拼接的字符串越長,加號連接符的性能越低儿咱。
- 在沒有特殊原因的情況下庭砍,盡量使用基本數(shù)據(jù)類型來代替封裝數(shù)據(jù)類型,int比Integer要更加有效混埠,其它數(shù)據(jù)類型也是一樣怠缸。
2.2 靜態(tài)優(yōu)于抽象
如果你并不需要訪問一個對系那個對象中的某些字段,只是想調(diào)用它的某些方法來去完成一項通用的功能钳宪,那么可以將這個方法設(shè)置成靜態(tài)方法揭北,調(diào)用速度提升15%-20%,同時也不用為了調(diào)用這個方法去專門創(chuàng)建對象了吏颖,也不用擔心調(diào)用這個方法后是否會改變對象的狀態(tài)(靜態(tài)方法無法訪問非靜態(tài)字段)罐呼。
2.3 對常量使用static final修飾符
final修飾之后,定義類不需要生成初始方法侦高,因為所有的常量都在dex文件的初始化器種進行初始化嫉柴。
2.4 多使用系統(tǒng)封裝好的API
系統(tǒng)提供不了的Api完成不了我們需要的功能才應(yīng)該自己去寫,因為使用系統(tǒng)的Api很多時候比我們自己寫的代碼要快得多奉呛,它們的很多功能都是通過底層的匯編模式執(zhí)行的计螺。 舉個例子,實現(xiàn)數(shù)組拷貝的功能瞧壮,使用循環(huán)的方式來對數(shù)組中的每一個元素一一進行賦值當然可行登馒,但是直接使用系統(tǒng)中提供的System.arraycopy()方法會讓執(zhí)行效率快9倍以上。
2.5 避免在內(nèi)部調(diào)用Getters/Setters方法
面向?qū)ο笾蟹庋b的思想是不要把類內(nèi)部的字段暴露給外部咆槽,而是提供特定的方法來允許外部操作相應(yīng)類的內(nèi)部字段陈轿。但在Android中,字段搜尋比方法調(diào)用效率高得多秦忿,我們直接訪問某個字段可能要比通過getters方法來去訪問這個字段快3到7倍麦射。但是編寫代碼還是要按照面向?qū)ο笏季S的,我們應(yīng)該在能優(yōu)化的地方進行優(yōu)化灯谣,比如避免在內(nèi)部調(diào)用getters/setters方法潜秋。
3. 布局優(yōu)化技巧
3.1 重用布局文件
標簽可以允許在一個布局當中引入另一個布局,那么比如說我們程序的所有界面都有一個公共的部分胎许,這個時候最好的做法就是將這個公共的部分提取到一個獨立的布局中峻呛,然后每個界面的布局文件當中來引用這個公共的布局罗售。
標簽是作為一種輔助擴展來使用的,它的主要作用是為了防止在引用布局文件時引用文件時產(chǎn)生多余的布局嵌套钩述。布局嵌套越多寨躁,解析起來就越耗時,性能就越差牙勘。因此編寫布局文件時應(yīng)該讓嵌套的層數(shù)越少越好职恳。
3.2 僅在需要時才加載布局
某個布局當中的元素不是一起顯示出來的,普通情況下只顯示部分常用的元素谜悟,而那些不常用的元素只有在用戶進行特定操作時才會顯示出來话肖。
可以使用ViewStub,ViewStub就是i一個寬高都為0的一個view葡幸,它默認不可見最筒,只有通過調(diào)用setVisibility函數(shù)或者Inflate才會加載布局。