1,UI優(yōu)化:這篇文章總結(jié)的不錯(cuò)
2族展,內(nèi)存泄漏優(yōu)化
常見(jiàn)的幾種形式:
資源對(duì)象沒(méi)關(guān)閉造成的內(nèi)存泄漏:
資源對(duì)象沒(méi)關(guān)閉造成的內(nèi)存泄漏 資源性對(duì)象比如(Cursor窝稿,F(xiàn)ile文件等)往往都用了一些緩沖从绘,我們?cè)诓皇褂玫臅r(shí)候,應(yīng)該及時(shí)關(guān)閉它們撵幽,以便它們的緩沖及時(shí)回收內(nèi)存碉克。它們的緩沖不僅存在于java虛擬機(jī)內(nèi),還存在于java虛擬機(jī)外并齐。如果我們僅僅是把它的引用設(shè)置為null,而不關(guān)閉它們漏麦,往往會(huì)造成內(nèi)存泄漏。因?yàn)橛行┵Y源性對(duì)象况褪,比如SQLiteCursor(在析構(gòu)函數(shù)finalize(),如果我們沒(méi)有關(guān)閉它撕贞,它自己會(huì)調(diào)close()關(guān)閉),如果我們沒(méi)有關(guān)閉它测垛,系統(tǒng)在回收它時(shí)也會(huì)關(guān)閉它捏膨,但是這樣的效率太低了。因此對(duì)于資源性對(duì)象在不使用的時(shí)候食侮,應(yīng)該調(diào)用它的close()函數(shù)号涯,將其關(guān)閉掉,然后才置為null.在我們的程序退出時(shí)一定要確保我們的資源性對(duì)象已經(jīng)關(guān)閉锯七。
構(gòu)造Adapter時(shí)链快,沒(méi)有使用緩存的convertView:
以構(gòu)造ListView的BaseAdapter為例,在BaseAdapter中提供了方法: public View getView(int position, ViewconvertView, ViewGroup parent) 來(lái)向ListView提供每一個(gè)item所需要的view對(duì)象眉尸。初始時(shí)ListView會(huì)從BaseAdapter中根據(jù)當(dāng)前的屏幕布局實(shí)例化一定數(shù)量的view對(duì)象域蜗,同時(shí)ListView會(huì)將這些view對(duì)象緩存起來(lái)。當(dāng)向上滾動(dòng)ListView時(shí)噪猾,原先位于最上面的list item的view對(duì)象會(huì)被回收霉祸,然后被用來(lái)構(gòu)造新出現(xiàn)的最下面的list item。這個(gè)構(gòu)造過(guò)程就是由getView()方法完成的袱蜡,getView()的第二個(gè)形參View convertView就是被緩存起來(lái)的list item的view對(duì)象(初始化時(shí)緩存中沒(méi)有view對(duì)象則convertView是null)丝蹭。由此可以看出,如果我們不去使用convertView坪蚁,而是每次都在getView()中重新實(shí)例化一個(gè)View對(duì)象的話奔穿,即浪費(fèi)資源也浪費(fèi)時(shí)間,也會(huì)使得內(nèi)存占用越來(lái)越大
Bitmap對(duì)象不在使用時(shí)調(diào)用recycle()釋放內(nèi)存:
有時(shí)我們會(huì)手工的操作Bitmap對(duì)象迅细,如果一個(gè)Bitmap對(duì)象比較占內(nèi)存巫橄,當(dāng)它不在被使用的時(shí)候淘邻,可以調(diào)用Bitmap.recycle()方法回收此對(duì)象的像素所占用的內(nèi)存 如何解決bitmap帶給我們的內(nèi)存問(wèn)題 1. 及時(shí)銷毀茵典,在用完Bitmap時(shí),要及時(shí)的recycle掉宾舅。 2. 設(shè)置一定的采樣率统阿,有時(shí)候彩倚,我們要顯示的區(qū)域很小,沒(méi)有必要將整個(gè)圖片都加載出來(lái)扶平,而只需要記載一個(gè)縮小過(guò)的圖片帆离,這時(shí)候可以設(shè)置一定的采樣率,那么就可以大大減小占用的內(nèi)存 3. 巧妙的運(yùn)用軟引用结澄,有些時(shí)候哥谷,我們使用Bitmap后沒(méi)有保留對(duì)它的引用,因此就無(wú)法調(diào)用Recycle函數(shù)麻献。這時(shí)候巧妙的運(yùn)用軟引用们妥,可以使Bitmap在內(nèi)存快不足時(shí)得到有效的釋放。
試著使用關(guān)于application的context來(lái)替代和activity相關(guān)的 context:
不要對(duì)activity 的context 長(zhǎng)期引用( 一個(gè)activity 的引用的生存周期應(yīng)該和activity 的生命周期相同) · 試著使用關(guān)于application的 context 來(lái)替代和activity相關(guān)的context · 如果一個(gè)acitivity 的非靜態(tài)內(nèi)部類的生命周期不受控制勉吻,那么避免使用它监婶;使用一個(gè)靜態(tài)的內(nèi)部類并且對(duì)其中的activity 使用一個(gè)弱引用。解決這個(gè)問(wèn)題的方法是使用一個(gè)靜態(tài)的內(nèi)部類齿桃,并且對(duì)它的外部類有一WeakReference惑惶,就像在ViewRoot中內(nèi)部類W所做的就是這么個(gè)例子。 · 垃圾回收器不能處理內(nèi)存泄漏的保障短纵。 - 注冊(cè)沒(méi)取消造成的內(nèi)存泄漏 所以建議:register放到onCreate带污,且onDestroy里無(wú)需做判斷,直接unregister香到。目的是注冊(cè)/反注冊(cè)在相反的生命周期中處理(onCreate->onDestroy刮刑、onStart->onStop),同時(shí)养渴,嚴(yán)格保證一定能反注冊(cè)雷绢。
集合中對(duì)象沒(méi)清理造成的內(nèi)存泄漏
我們通常把一些對(duì)象的引用加入到了集合中,當(dāng)我們不需要該對(duì)象時(shí)理卑,并沒(méi)有把它的引用從集合中清理掉翘紊,這樣這個(gè)集合就會(huì)越來(lái)越大。如果這個(gè)集合是static的話藐唠,那情況就更嚴(yán)重了帆疟。 - static的不合理應(yīng)用導(dǎo)致的內(nèi)存泄漏 public class ClassName {
private static Context mContext; //省略 } 以上的代碼是很危險(xiǎn)的,如果將Activity賦值到么mContext的話宇立。那么即使該Activity已經(jīng)onDestroy踪宠,但是由于仍有對(duì)象保存它的引用,因此該Activity依然不會(huì)被釋放. 如何才能有效的避免這種引用的發(fā)生呢妈嘹? 第一柳琢,應(yīng)該盡量避免static成員變量引用資源耗費(fèi)過(guò)多的實(shí)例,比如Context。 第二柬脸、Context盡量使用Application Context他去,因?yàn)锳pplication的Context的生命周期比較長(zhǎng),引用它不會(huì)出現(xiàn)內(nèi)存泄露的問(wèn)題倒堕。 第三灾测、使用WeakReference代替強(qiáng)引用。比如可以使用WeakReferencemContextRef;
線程惹的禍
線程也是造成內(nèi)存泄露的一個(gè)重要的源頭垦巴。線程產(chǎn)生內(nèi)存泄露的主要原因在于線程生命周期的不可控
簡(jiǎn)單介紹LeakCanary原理
2.1媳搪,注意只支持android4.0以上
2.2,ActivityRefWatcher中骤宣,注冊(cè)Activity生命周期監(jiān)聽(tīng)接口蛾号,當(dāng)Activity onDestroy()被調(diào)用時(shí),將當(dāng)前Activity加入內(nèi)存泄漏監(jiān)聽(tīng)隊(duì)列涯雅;
2.3鲜结,WeakReference和ReferenceQueue配合使用,如果弱引用所引用的對(duì)象被垃圾回收活逆,Java虛擬機(jī)就會(huì)把這個(gè)弱引用加入到與之關(guān)聯(lián)的引用隊(duì)列
2.4精刷,檢測(cè)方法就很簡(jiǎn)單了,主動(dòng)GC蔗候,觸發(fā)WeakReference被GC怒允,同時(shí)檢測(cè)GC前后,ReferenceQueue是否包含被監(jiān)聽(tīng)對(duì)象锈遥;如果不包含纫事,則說(shuō)明該對(duì)象沒(méi)有被GC,一定存在到GC Roots的強(qiáng)引用鏈所灸,也就是發(fā)生了泄漏
3丽惶,減小apk的大小優(yōu)化
3.1,理解apk的結(jié)構(gòu)
在討論怎樣減少應(yīng)用大小之前爬立,先了解APK的結(jié)構(gòu)是有用的钾唬。一個(gè)APK文件就是ZIP包,其中包含了組成你的應(yīng)用的所有文件侠驯,比如Java類文件抡秆,資源文件,和一個(gè)包含被編譯資源的文件吟策。 一個(gè)APK包含了一下目錄: - META-INF/: 包含CERT.SF和CERT.RSA簽名文件儒士,也包含了MANIFEST.MF文件。(譯注:校驗(yàn)這個(gè)APK是否被人改動(dòng)過(guò)) - assets/: 包含了應(yīng)用的資源檩坚,這些資源能夠通過(guò)AssetManager對(duì)象獲得着撩。 - res/: 包含了沒(méi)被被編譯到resources.arsc的資源诅福。 - lib/: 包含了針對(duì)處理器層面的被編譯的代碼。這個(gè)目錄針對(duì)每個(gè)平臺(tái)類型都有一個(gè)子目錄睹酌,比如armeabi, armeabi-v7a, arm64-v8a, x86, x86_64和mips权谁。
一個(gè)APK也包含了一下文件剩檀,其中只有AndroidManifest.XML是強(qiáng)制的: - resources.arsc: 包含了被編譯的資源憋沿。該文件包含了res/values目錄的所有配置的XML內(nèi)容。打包工具將XML內(nèi)容編譯成二進(jìn)制形式并壓縮沪猴。這些內(nèi)容包含了語(yǔ)言字符串和styles辐啄,還包含了那些內(nèi)容雖然不直接存儲(chǔ)在resources.arsc文件中,但是給定了該內(nèi)容的路徑运嗜,比如布局文件和圖片壶辜。 - classes.dex: 包含了能被Dalvik/Art虛擬機(jī)理解的DEX文件格式的類。 - AndroidManifest.xml: 包含了主要的Android配置文件担租。這個(gè)文件列出了應(yīng)用名稱砸民、版本、訪問(wèn)權(quán)限奋救、引用的庫(kù)文件岭参。該文件使用二進(jìn)制XML格式存儲(chǔ)。(譯注:該文件還能看到應(yīng)用的minSdkVersion, targetSdkVersion等信息)
3.2尝艘,如何減小apk大小
3.2.1演侯,移除不使用的資源
lint是Android Studio中的一個(gè)靜態(tài)代碼分析工具,檢測(cè)在”res/“目錄中你的代碼沒(méi)有引用的資源背亥。當(dāng)lint工具發(fā)現(xiàn)了項(xiàng)目中潛在的未使用的資源秒际,它會(huì)打印以下類似信息: res/layout/preferences.xml: Warning: The resource R.layout.preferences appears to be unused [UnusedResources] 被引用的庫(kù)中可能會(huì)包含沒(méi)使用的資源。如果你在build.gradle文件中啟用shrinkResources狡汉,則Gradle能自動(dòng)移除這些資源娄徊。
為了使用shrinkResources,你必須要啟用代碼混淆盾戴。在構(gòu)建過(guò)程中嵌莉,首先proguard移除了未使用的代碼,然后gradle移除未使用的資源捻脖。 在Gradle插件0.7或更高版本锐峭,你能申明應(yīng)用支持的配置。Gradle通過(guò)傳遞resConfigs和defaultConfig給構(gòu)建系統(tǒng)可婶,構(gòu)建系統(tǒng)會(huì)防止不支持的配置出現(xiàn)在APK中沿癞,從而減少APK大小。
3.2.2矛渴,最小化第三方庫(kù)中資源的使用
當(dāng)開(kāi)發(fā)Android應(yīng)用時(shí)椎扬,你經(jīng)常使用第三方庫(kù)提升應(yīng)用的可用性和靈活性惫搏。比如,你引用Android Support Library提升舊設(shè)備的用戶體驗(yàn)蚕涤,或者使用Google Play服務(wù)實(shí)現(xiàn)文字自動(dòng)翻譯筐赔。
如果一個(gè)第三方庫(kù)原本是為服務(wù)器或普通電腦設(shè)計(jì),會(huì)引入許多不需要的對(duì)象和方法揖铜。為了只引入應(yīng)用需要的庫(kù)中的那部分茴丰,你可以編輯庫(kù)文件(如果庫(kù)的license允許你這么做)。你也能使用另外的針對(duì)手機(jī)的實(shí)現(xiàn)同樣功能的庫(kù)天吓。 注意:代碼混淆能清除庫(kù)中不被使用的代碼贿肩,但是他不能移除庫(kù)的大量?jī)?nèi)部依賴。
3.2.3龄寞,只支持部分屏幕密度
Android支持很多設(shè)備集汰规,其中包含了各種不同的屏幕密度。在Android 4.4及更高版本物邑,框架支持不同的密度:ldpi, mdpi, tvdpi, hdpi, xhdpi, xxhdpi和xxxhdpi溜哮。盡管Android支持所有這些屏幕密度,但你不需要為每個(gè)密度都配置相應(yīng)的資源色解。
如果你知道某種特定屏幕密度已經(jīng)很少有用戶使用了茂嗓,那么你可以考慮是否需要為這個(gè)屏幕密度配置資源。如果你不包含針對(duì)特定屏幕密度的資源冒签,那么Android會(huì)自動(dòng)縮放原本針對(duì)其他密度的已有資源在抛。
如果你的應(yīng)用只需要縮放的圖片,你甚至可以把圖片存放在drawable-nodpi目錄萧恕,從而節(jié)省更多空間刚梭。我們推薦每個(gè)應(yīng)用都應(yīng)該至少包含xxhdpi的圖片。
3.2.4票唆,減少動(dòng)畫幀數(shù)
使用幀動(dòng)畫會(huì)大大增加APK的大小朴读。圖1顯示了目錄中構(gòu)成幀動(dòng)畫的多個(gè)PNG文件。每個(gè)圖片都是動(dòng)畫的一幀走趋。
對(duì)于加入動(dòng)畫的每幀衅金,你都增加了APK中圖片的個(gè)數(shù)。圖1中簿煌,幀動(dòng)畫的幀率是30 FPS氮唯。如果幀率降到15 FPS,圖片數(shù)量將減少一半姨伟。
3.2.5惩琉,使用drawable對(duì)象
一些圖片不需要靜態(tài)的圖片資源,框架能在運(yùn)行時(shí)動(dòng)態(tài)地繪制圖像夺荒。Drawable對(duì)象(XML的)只需要占用APK中的一點(diǎn)空間瞒渠。另外良蒸,XML形式的Drawable對(duì)象能夠產(chǎn)生遵循Material Design設(shè)計(jì)規(guī)范的圖像。
3.2.6伍玖,重(chong第二聲)用資源
你能包含一張圖片的很多變種嫩痰,比如染色、陰影窍箍、旋轉(zhuǎn)的版本串纺。但是,我們推薦在運(yùn)行時(shí)復(fù)用一張圖片來(lái)定制化他們仔燕。
Android提供了很多方式改變資源的顏色造垛。對(duì)于Android 5.0及以上魔招,使用android:tint和tintMode屬性晰搀。對(duì)于更低版本,使用ColorFilter類办斑。
你也能夠刪除那些只是對(duì)另一個(gè)資源做旋轉(zhuǎn)的資源外恕。下面的代碼片段提供了對(duì)一個(gè)箭頭旋轉(zhuǎn)180度。 arrowexpand" android:fromdegrees="180" android:pivotx="50%" android:pivoty="50%" android:todegrees="180">
3.2.7乡翅,通過(guò)代碼繪制
你也能通過(guò)代碼繪制圖像鳞疲,從而減少APK大小。代碼方式繪制圖像不需要任何空間因?yàn)槟悴辉傩枰贏PK中存儲(chǔ)圖像文件蠕蚜。
3.2.8尚洽,壓縮PNG文件
AAPT工具能夠在構(gòu)建過(guò)程中通過(guò)無(wú)損壓縮優(yōu)化res/drawable/中的圖片資源。比如aapt工具能將需要顏色少于256色的PNG變?yōu)?位PNG圖靶累,這樣能夠在保證圖片質(zhì)量的同時(shí)減少內(nèi)存使用腺毫。
需要注意aapt有以下局限性: - aapt工具不會(huì)壓縮asset目錄的PNG文件。 - 通過(guò)aapt的優(yōu)化挣柬,圖片文件會(huì)使用少于256色潮酒。 - aapt工具可能會(huì)影響已經(jīng)被壓縮過(guò)的PNG文件。為了防止這種情況邪蛔,你可以在gradle文件中設(shè)置cruncherEnabled為false禁用aapt對(duì)PNG的壓縮急黎。 aaptOptions { cruncherEnabled = false } 譯注:建議把cruncherEnabled設(shè)為false,然后通過(guò)tinypng手工壓縮PNG圖片侧到。
3.2.9勃教,壓縮PNG和JPEG文件
你能使用一些工具(比如pngcrush, pngquant, zopflipng)在不降低圖像質(zhì)量的前提下減少PNG文件大小。所有這些工具都能保留圖像質(zhì)量的情況下減少PNG文件大小匠抗。
pngcrush工具特別有效:這個(gè)工具通過(guò)迭代png過(guò)濾器和zlib參數(shù)故源,使用每種過(guò)濾器和參數(shù)的組合壓縮圖像,并選擇最小的那個(gè)作為最后的輸出戈咳。
對(duì)于JPEG文件心软,能使用packJPG壓縮JPEG文件壕吹。 譯注:guetzli是Google最近推出的JPEG編碼器,官方宣稱相同圖片質(zhì)量時(shí)删铃,比libjpeg生成的圖片小20-30%耳贬。
3.2.10,使用WebP文件格式
你也能使用WebP文件格式存儲(chǔ)圖片而不是PNG或者JPEG猎唁。WebP格式是有損壓縮(像JPEG)且有透明通道(像PNG)咒劲,且壓縮率高于JPEG或PNG。
使用WebP文件格式也有一些缺點(diǎn)诫隅。第一腐魂,低于Android 3.2的版本不支持WebP,第二逐纬,WebP的解碼時(shí)間比PNG長(zhǎng)蛔屹。
3.2.11,使用向量圖
你能使用向量圖去創(chuàng)建一個(gè)分辨率無(wú)關(guān)的圖標(biāo)豁生。使用向量圖能夠顯著減少APK大小兔毒。在Android中向量圖是以VectorDrawable對(duì)象形式存在的。使用VectorDrawable對(duì)象甸箱,一個(gè)100B的文件能生成一個(gè)屏幕大小的清晰圖片育叁。
但是,系統(tǒng)需要很長(zhǎng)時(shí)間渲染VectorDrawable對(duì)象芍殖,更大的圖片需要更長(zhǎng)的時(shí)間顯示在屏幕上豪嗽。因此只有小圖片才考慮使用向量圖。
3.2.12減少Native和Java代碼
確保理解任何自動(dòng)生成的代碼豌骏。比如龟梦,許多protocol buffer工具生成了過(guò)多的方法和類,這會(huì)讓你的應(yīng)用大小翻倍肯适。
3.2.13变秦,移除調(diào)試符號(hào)
如果應(yīng)用在開(kāi)發(fā)中并且仍需要調(diào)試,那么我們能理解使用調(diào)試符號(hào)框舔。使用Android NDK提供的arm-eabi-strip工具蹦玫,能從Native庫(kù)中刪除不必要的調(diào)試符號(hào),之后你再編譯release包
3.2.14刘绣,避免抽取Native庫(kù)
在APK中存儲(chǔ)未壓縮的so文件樱溉,并且在Manifest文件的中設(shè)置android:extractNativeLibs為false,這會(huì)防止在安裝時(shí)PackageManager將APK中的so文件拷貝到文件系統(tǒng)纬凤,避免這種拷貝會(huì)讓應(yīng)用在做增量更新時(shí)的更新包更小福贞。
3.2.15,維持多個(gè)小的APK包
你的APK會(huì)包含用戶下載了但從未使用的內(nèi)容停士,比如地區(qū)或語(yǔ)言信息(譯注:比如我是中國(guó)人挖帘,我就不會(huì)用到其他語(yǔ)種的資源)完丽。為了給用戶創(chuàng)建小的下載包,你能把你的應(yīng)用拆分成多個(gè)APK拇舀,這些APK的差別在于一些因素(比如屏幕大小或者GPU紋理支持)逻族。
當(dāng)一個(gè)用戶下載了應(yīng)用,設(shè)備根據(jù)自身的特性和設(shè)置獲取正確的APK骄崩。這種方式能夠讓設(shè)備不獲取設(shè)備不需要的資源聘鳞。比如,如果設(shè)備是hdpi的要拂,那么他就不需要xxxhdpi的資源抠璃。
其他優(yōu)化后面記錄,這里推薦另一個(gè)優(yōu)化系列脱惰,寫的很細(xì)致