一、ANR(Application Not Responding)問題
1、什么是ANR(Application Not Responding)
ANR就是一個(gè)程序無響應(yīng)的對(duì)話框
在一個(gè)Activity(Service)當(dāng)中最長(zhǎng)的執(zhí)行時(shí)間是5秒泳挥,超出5秒無響應(yīng)就會(huì)導(dǎo)致ANR
在一個(gè)BroadcastReceiver當(dāng)中最長(zhǎng)的執(zhí)行時(shí)間是10秒,
原因:在主線程中做了耗時(shí)操作,所以才會(huì)導(dǎo)致ANR的彈框
2、造成ANR的主要原因
應(yīng)用程序的響應(yīng)性是由Activity Manager和WindowManager系統(tǒng)服務(wù)監(jiān)視的太伊,當(dāng)它監(jiān)測(cè)到Activity和BroadcastReceiver當(dāng)中5秒、10秒沒有執(zhí)行完任務(wù)之后逛钻。安卓就會(huì)彈出ANR的對(duì)話框
- 主線程被IO操作(從4.0之后網(wǎng)絡(luò)IO不允許在主線程中)阻塞
- 主線程中存在耗時(shí)操作
3僚焦、造成ANR的主要原因-Android中哪些操作是在主線程呢?
- Activity的所有生命周期回調(diào)都是執(zhí)行在主線程的
- Service默認(rèn)是執(zhí)行在主線程的
- BroadcastReceiver的onReceive回調(diào)是執(zhí)行在主線程的
- 沒有使用子線程的looper的Handler的handlerMessage曙痘,post(Runnable)是執(zhí)行在主線程的
- AsyncTask的回調(diào)中除了doInBackground芳悲,其他都是執(zhí)行在主線程
4、如何解決ANR
- 使用AsyncTask處理耗時(shí)IO操作
- 使用Thread或者HandlerThread提高優(yōu)先級(jí)边坤,不提高優(yōu)先級(jí) 它的優(yōu)先級(jí)和主線程的優(yōu)先級(jí)是一樣的仍然會(huì)造成ANR
- 使用handler來處理工作線程的耗時(shí)任務(wù)
- 在Activity的onCreate和onResume回調(diào)中盡量避免耗時(shí)的操作
二名扛、OOM(Out of Memory)問題
1、什么是oom
當(dāng)前占用的內(nèi)存加上我們申請(qǐng)的內(nèi)存資源超過了Dalvik虛擬機(jī)的最大內(nèi)存限制就會(huì)拋出Out of memory異常惩嘉,最常見的oom就是bitmap加載大圖的時(shí)候罢洲。
2、一些容易混淆的概念
- 內(nèi)存溢出:就是Out of memory
- 內(nèi)存抖動(dòng):短時(shí)間內(nèi)大量的對(duì)象被創(chuàng)建又被馬上釋放文黎,瞬間產(chǎn)生的對(duì)象。會(huì)嚴(yán)重占用內(nèi)存區(qū)域
- 內(nèi)存泄露:進(jìn)程中的某些對(duì)象殿较,比如說垃圾對(duì)象耸峭。已經(jīng)沒有被其他對(duì)象引用到了,但是它們確可以直接或間接引用到GCRoots淋纲。導(dǎo)致GC無法直接產(chǎn)生作用劳闹,一旦內(nèi)存泄露累積到一定程度,就會(huì)引起內(nèi)存溢出。
3本涕、如何解決oom
1业汰、有關(guān)bitmap優(yōu)化
- 圖片的顯示:比如ListView滑動(dòng)的時(shí)候要顯示縮略圖不要去網(wǎng)絡(luò)請(qǐng)求下載圖片,只有監(jiān)聽到ListView停止滑動(dòng)的時(shí)候再去加載網(wǎng)絡(luò)大圖
- 及時(shí)釋放內(nèi)存
- 圖片壓縮
- inBitmap屬性
- 捕獲異常
2菩颖、ListView
- ConverView/Lru機(jī)制緩存bitmap
- 避免在onDraw方法里面執(zhí)行對(duì)象的創(chuàng)建样漆,頻繁的創(chuàng)建對(duì)象容易引起內(nèi)存抖動(dòng)
- 謹(jǐn)慎使用多進(jìn)程
三、Bitmap相關(guān)問題
bitmap是存在native內(nèi)存和Java內(nèi)存當(dāng)中的晦闰,當(dāng)被回收的時(shí)候分兩部分回收放祟。一是回收J(rèn)ava內(nèi)存當(dāng)中的內(nèi)存二是回收native內(nèi)存當(dāng)中的內(nèi)存。
1呻右、Recycle
recycle釋放bitmap內(nèi)存的時(shí)候跪妥,會(huì)釋放和這個(gè)bitmap有關(guān)的native內(nèi)存,同時(shí)會(huì)清理有關(guān)數(shù)據(jù)對(duì)象的引用声滥。但不是立即清理眉撵,它會(huì)給垃圾回收器發(fā)送消息指令。讓它在沒有其他對(duì)象引用這個(gè)bitmap對(duì)象的時(shí)候落塑,進(jìn)行垃圾回收纽疟。
當(dāng)bitmap調(diào)用recycle之后,bitmap會(huì)被標(biāo)記為“dead”芜赌。這個(gè)時(shí)候你再調(diào)用bitmap的其他方法就會(huì)引起異常仰挣。比如getPixels()或者setPixels(),同時(shí)recycle操作是不可逆的。所以你要確定被recycle之后不再調(diào)用這個(gè)bitmap對(duì)象以及它的任何方法缠沈,否則就會(huì)引起異常膘壶。官方建議我們不主動(dòng)調(diào)用recycle方法,當(dāng)沒有對(duì)象引用這個(gè)bitmap的時(shí)候垃圾回收器會(huì)主動(dòng)的回收這個(gè)對(duì)象洲愤。
2颓芭、LRU算法
- lru算法是最近最少使用的對(duì)象,我們把它清除出緩存隊(duì)列柬赐。
- LruCache是一個(gè)泛型類亡问,lru算法內(nèi)部使用了一個(gè)LinkedHashMap來實(shí)現(xiàn)的,并且提供了get和put方法來完成緩存的添加和獲取操作肛宋,當(dāng)緩存滿的時(shí)候它內(nèi)部提供了一個(gè)trimToSize()的方法州藕。把較早和最少使用的的緩存對(duì)象移除并添加新的緩存對(duì)象。
3酝陈、計(jì)算inSampleSize
// 根據(jù)maxWidth, maxHeight計(jì)算最合適的inSampleSize
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// 圖像的原始高度和寬度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
4床玻、縮略圖
//縮略圖
public static Bitmap thumbnail(String path,
int maxWidth, int maxHeight, boolean autoRotate) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
// 獲取這個(gè)圖片的寬和高信息到options中, 此時(shí)返回bm為空
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
options.inJustDecodeBounds = false;
// 計(jì)算縮放比
int sampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
options.inSampleSize = sampleSize;
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inPurgeable = true;
options.inInputShareable = true;
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
bitmap = BitmapFactory.decodeFile(path, options);
return bitmap;
}
5、三級(jí)緩存
- 網(wǎng)絡(luò)緩存
- 本地緩存
- 內(nèi)存緩存
四沉帮、UI卡頓問題
1锈死、UI卡頓原理
UI渲染系統(tǒng)做了太多的耗時(shí)操作贫堰,和執(zhí)行了大量的動(dòng)畫。
60fps->16ms
過渡繪制:UI布局中有大量重疊的部分待牵,多層次的UI結(jié)構(gòu)中其屏。
2.UI卡頓的原因分析
- 人為在UI線程中做輕微耗時(shí)操作,導(dǎo)致UI線程卡頓
- 布局Layout過于復(fù)雜缨该,無法在16ms內(nèi)完成渲染
- 同一時(shí)間動(dòng)畫執(zhí)行的次數(shù)過多偎行,導(dǎo)致CPU或GPU負(fù)載過重
- View過渡繪制,導(dǎo)致某些像素在同一幀時(shí)間內(nèi)被繪制多次压彭,從而使CPU或GPU負(fù)載過重
- View頻繁的觸發(fā)measure睦优、layout,導(dǎo)致measure壮不、layout累計(jì)耗時(shí)過多及整個(gè)View頻繁的重新渲染
- 內(nèi)存頻繁觸發(fā)GC過多汗盘,導(dǎo)致暫時(shí)阻塞渲染操作
- 冗余資源及邏輯等導(dǎo)致加載和執(zhí)行緩慢
- ANR
3、UI卡頓總結(jié)
- 布局優(yōu)化:使用常見的include询一、merge隐孽、ViewStub標(biāo)簽。盡量不存在冗余嵌套或者復(fù)雜的布局
- 列表及Adapter優(yōu)化
- 背景和圖片等內(nèi)存分配優(yōu)化
- 避免ANR
五健蕊、內(nèi)存泄露問題
1蝗砾、java內(nèi)存的分配策略
- 靜態(tài)存儲(chǔ)區(qū)(方法區(qū))
存放靜態(tài)數(shù)據(jù)缔赠、全局變量聂宾。程序編譯的時(shí)候已經(jīng)分配好了箫章,在靜態(tài)存儲(chǔ)區(qū)存儲(chǔ)的變量,在程序運(yùn)行的整個(gè)期間都會(huì)存在 - 棧區(qū)
方法內(nèi)的局部變量嫡锌,會(huì)在棧上創(chuàng)建存儲(chǔ)空間虑稼。并在方法結(jié)束后,這些在變量所持有的內(nèi)存會(huì)被自動(dòng)釋放 - 堆區(qū)
又稱動(dòng)態(tài)內(nèi)存分配势木,通常就是我們new對(duì)象出來的內(nèi)存蛛倦。這部分內(nèi)存在不使用的時(shí)候會(huì)有java的內(nèi)存回收器進(jìn)行回收
2、java中的內(nèi)存泄露
內(nèi)存泄露是指無用對(duì)象(不再使用的對(duì)象)持續(xù)占有內(nèi)存或無用對(duì)象的內(nèi)存得不到及時(shí)釋放啦桌,從而造成的內(nèi)存空間的浪費(fèi)稱為內(nèi)存泄露溯壶。
六、Android內(nèi)存管理機(jī)制
1甫男、分配機(jī)制
操作系統(tǒng)會(huì)為每個(gè)進(jìn)程分配一個(gè)合理大小的內(nèi)存且改,從而保證每一個(gè)進(jìn)程能夠正常的運(yùn)行。而不至于內(nèi)存不夠使用或者每個(gè)進(jìn)程占用太多的內(nèi)存
2板驳、回收機(jī)制
在系統(tǒng)內(nèi)存不夠的時(shí)候钾虐,他會(huì)有一個(gè)合理的回收再分配機(jī)制從而保證新的進(jìn)程能夠正常的運(yùn)行。
3笋庄、內(nèi)存管理機(jī)制的特點(diǎn)
- 更少的占用內(nèi)存
- 在合適的時(shí)候效扫,合理的釋放系統(tǒng)資源
- 在系統(tǒng)內(nèi)存緊張的情況下,能釋放大部分不重要的資源直砂,來為Android系統(tǒng)提供可用的內(nèi)存
- 能夠很合理的在特殊生命周期菌仁,保存或者還原重要數(shù)據(jù),以至于系統(tǒng)能夠保證正確的重新恢復(fù)該應(yīng)用
4静暂、內(nèi)存優(yōu)化的方法
- 當(dāng)Service完成任務(wù)后济丘,盡量停止它。推薦使用Intentservice
- 在UI不可見的時(shí)候洽蛀,釋放掉一些只有UI使用的資源
- 在系統(tǒng)內(nèi)存緊張的時(shí)候摹迷,盡可能多的釋放掉一些非重要資源
- 避免濫用Bitmap導(dǎo)致的內(nèi)存浪費(fèi)
- 使用針對(duì)內(nèi)存優(yōu)化過的數(shù)據(jù)容器:SparseArray
- 避免使用依賴注入的框架
- 使用ZIP對(duì)齊的APK
- 使用多進(jìn)程
七、冷啟動(dòng)優(yōu)化
1郊供、冷啟動(dòng)的定義
冷啟動(dòng)就是在啟動(dòng)應(yīng)用前峡碉,系統(tǒng)中沒有該應(yīng)用的任何進(jìn)程信息
2、冷啟動(dòng)和熱啟動(dòng)的區(qū)別
熱啟動(dòng):用戶使用返回鍵退出應(yīng)用驮审,然后馬上又重新啟動(dòng)應(yīng)用鲫寄。熱啟動(dòng)的應(yīng)用的進(jìn)程是保留在后臺(tái)的
- 冷啟動(dòng):每次啟動(dòng)的時(shí)候都會(huì)走Application這個(gè)類,
- 熱啟動(dòng):進(jìn)程中保留留這個(gè)app的進(jìn)行疯淫,它會(huì)直接走M(jìn)ainActivity這個(gè)類
3地来、冷啟動(dòng)的流程
- Zygote進(jìn)程中fork創(chuàng)建出一個(gè)新的進(jìn)程
- 創(chuàng)建和初始化Application類、創(chuàng)建MainActivity類inflate布局
- 當(dāng)onCreate/onStart/onResume方法都走完
- contentView的measure/layout/draw顯示在界面上
4熙掺、冷啟動(dòng)流程-總結(jié)
Application的構(gòu)造方法->attachBaseContext()->onCreate()->Activity的構(gòu)造方法->onCreate()->配置主題中背景等屬性->onStart()->onResume()->測(cè)量布局繪制顯示在界面上
5未斑、 如何對(duì)冷啟動(dòng)的時(shí)間進(jìn)行優(yōu)化
- 減少onCreate()方法的工作量
- 不要讓Application參與業(yè)務(wù)的操作
- 不要在Application進(jìn)行耗時(shí)操作
- 不要以靜態(tài)變量的方式在Application中保存數(shù)據(jù)
- 布局(減少布局的復(fù)雜性)、mainThread(資源的初始化放到子線程當(dāng)中)
八币绩、其他優(yōu)化問題
1蜡秽、Android不用靜態(tài)變量?jī)?chǔ)存數(shù)據(jù)
- 靜態(tài)變量等數(shù)據(jù)由于進(jìn)程已經(jīng)被殺死而被從新初始化
- 使用其他數(shù)據(jù)傳輸方式:文件、SP类浪、contentProvider,要對(duì)數(shù)據(jù)進(jìn)行非空判斷
2载城、有關(guān)SharePreference問題
- 不能跨進(jìn)程同步
- 存儲(chǔ)SharePreference的文件過大問題,文件過大獲取值得時(shí)候费就,有可能阻塞主線程诉瓦。解析很大的SharePreference文件的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)文件對(duì)象,導(dǎo)致垃圾回收機(jī)制頻繁的進(jìn)行垃圾回收力细。容易造成UI卡頓和內(nèi)存抖動(dòng)
3睬澡、內(nèi)存對(duì)象的序列化
序列化:將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以儲(chǔ)存或傳輸?shù)男问降倪^程
- Serializeble:Serializeble在序列化的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)變量,從而引起頻繁的垃圾回收眠蚂。影響UI卡頓煞聪,容易引起內(nèi)存抖動(dòng),造成oom
- Parcelable:不能使用存儲(chǔ)在磁盤上的文件
4逝慧、 內(nèi)存對(duì)象序列化-總結(jié)
- Serializeble是java的序列化方式昔脯,Parcelable是Android特有的序列化方式
- 在使用內(nèi)存的時(shí)候啄糙,Parcelable比Serializeble性能高
- Serializeble在序列化的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)變量,從而引起頻繁的GC
- Parcelable不能使用在要將數(shù)據(jù)存儲(chǔ)在磁盤上的情況