Android異常與性能優(yōu)化問(wèn)題

一、ANR(Application Not Responding)問(wèn)題

1、什么是ANR(Application Not Responding)
ANR就是一個(gè)程序無(wú)響應(yīng)的對(duì)話(huà)框

在一個(gè)Activity(Service)當(dāng)中最長(zhǎng)的執(zhí)行時(shí)間是5秒,超出5秒無(wú)響應(yīng)就會(huì)導(dǎo)致ANR
在一個(gè)BroadcastReceiver當(dāng)中最長(zhǎng)的執(zhí)行時(shí)間是10秒,
原因:在主線(xiàn)程中做了耗時(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秒沒(méi)有執(zhí)行完任務(wù)之后。安卓就會(huì)彈出ANR的對(duì)話(huà)框

  • 主線(xiàn)程被IO操作(從4.0之后網(wǎng)絡(luò)IO不允許在主線(xiàn)程中)阻塞
  • 主線(xiàn)程中存在耗時(shí)操作
3、造成ANR的主要原因-Android中哪些操作是在主線(xiàn)程呢?
  • Activity的所有生命周期回調(diào)都是執(zhí)行在主線(xiàn)程的
  • Service默認(rèn)是執(zhí)行在主線(xiàn)程的
  • BroadcastReceiver的onReceive回調(diào)是執(zhí)行在主線(xiàn)程的
  • 沒(méi)有使用子線(xiàn)程的looper的Handler的handlerMessage,post(Runnable)是執(zhí)行在主線(xiàn)程的
  • AsyncTask的回調(diào)中除了doInBackground,其他都是執(zhí)行在主線(xiàn)程
4、如何解決ANR
  • 使用AsyncTask處理耗時(shí)IO操作
  • 使用Thread或者HandlerThread提高優(yōu)先級(jí),不提高優(yōu)先級(jí) 它的優(yōu)先級(jí)和主線(xiàn)程的優(yōu)先級(jí)是一樣的仍然會(huì)造成ANR
  • 使用handler來(lái)處理工作線(xiàn)程的耗時(shí)任務(wù)
  • 在Activity的onCreate和onResume回調(diào)中盡量避免耗時(shí)的操作

二、OOM(Out of Memory)問(wèn)題

1、什么是oom

當(dāng)前占用的內(nèi)存加上我們申請(qǐng)的內(nèi)存資源超過(guò)了Dalvik虛擬機(jī)的最大內(nèi)存限制就會(huì)拋出Out of memory異常,最常見(jiàn)的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ì)象,比如說(shuō)垃圾對(duì)象。已經(jīng)沒(méi)有被其他對(duì)象引用到了,但是它們確可以直接或間接引用到GCRoots。導(dǎo)致GC無(wú)法直接產(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)聽(tīng)到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)問(wè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ā)送消息指令。讓它在沒(méi)有其他對(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)沒(méi)有對(duì)象引用這個(gè)bitmap的時(shí)候垃圾回收器會(huì)主動(dòng)的回收這個(gè)對(duì)象。

2、LRU算法
  • lru算法是最近最少使用的對(duì)象,我們把它清除出緩存隊(duì)列。
  • LruCache是一個(gè)泛型類(lèi),lru算法內(nèi)部使用了一個(gè)LinkedHashMap來(lái)實(shí)現(xiàn)的,并且提供了get和put方法來(lái)完成緩存的添加和獲取操作,當(dāng)緩存滿(mǎn)的時(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卡頓問(wèn)題

1、UI卡頓原理

UI渲染系統(tǒng)做了太多的耗時(shí)操作,和執(zhí)行了大量的動(dòng)畫(huà)。
60fps->16ms
過(guò)渡繪制:UI布局中有大量重疊的部分,多層次的UI結(jié)構(gòu)中。

2.UI卡頓的原因分析
  • 人為在UI線(xiàn)程中做輕微耗時(shí)操作,導(dǎo)致UI線(xiàn)程卡頓
  • 布局Layout過(guò)于復(fù)雜,無(wú)法在16ms內(nèi)完成渲染
  • 同一時(shí)間動(dòng)畫(huà)執(zhí)行的次數(shù)過(guò)多,導(dǎo)致CPU或GPU負(fù)載過(guò)重
  • View過(guò)渡繪制,導(dǎo)致某些像素在同一幀時(shí)間內(nèi)被繪制多次,從而使CPU或GPU負(fù)載過(guò)重
  • View頻繁的觸發(fā)measure、layout,導(dǎo)致measure、layout累計(jì)耗時(shí)過(guò)多及整個(gè)View頻繁的重新渲染
  • 內(nèi)存頻繁觸發(fā)GC過(guò)多,導(dǎo)致暫時(shí)阻塞渲染操作
  • 冗余資源及邏輯等導(dǎo)致加載和執(zhí)行緩慢
  • ANR
3、UI卡頓總結(jié)
  • 布局優(yōu)化:使用常見(jiàn)的include、merge、ViewStub標(biāo)簽。盡量不存在冗余嵌套或者復(fù)雜的布局
  • 列表及Adapter優(yōu)化
  • 背景和圖片等內(nèi)存分配優(yōu)化
  • 避免ANR

五、內(nèi)存泄露問(wèn)題

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ū)
    又稱(chēng)動(dòng)態(tài)內(nèi)存分配,通常就是我們new對(duì)象出來(lái)的內(nèi)存。這部分內(nèi)存在不使用的時(shí)候會(huì)有java的內(nèi)存回收器進(jìn)行回收
2、java中的內(nèi)存泄露

內(nèi)存泄露是指無(wú)用對(duì)象(不再使用的對(duì)象)持續(xù)占有內(nèi)存或無(wú)用對(duì)象的內(nèi)存得不到及時(shí)釋放,從而造成的內(nèi)存空間的浪費(fèi)稱(chēng)為內(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)存緊張的情況下,能釋放大部分不重要的資源,來(lái)為Android系統(tǒng)提供可用的內(nèi)存
  • 能夠很合理的在特殊生命周期,保存或者還原重要數(shù)據(jù),以至于系統(tǒng)能夠保證正確的重新恢復(fù)該應(yīng)用
4、內(nèi)存優(yōu)化的方法
  • 當(dāng)Service完成任務(wù)后,盡量停止它。推薦使用Intentservice
  • 在UI不可見(jiàn)的時(shí)候,釋放掉一些只有UI使用的資源
  • 在系統(tǒng)內(nèi)存緊張的時(shí)候,盡可能多的釋放掉一些非重要資源
  • 避免濫用Bitmap導(dǎo)致的內(nèi)存浪費(fèi)
  • 使用針對(duì)內(nèi)存優(yōu)化過(guò)的數(shù)據(jù)容器:SparseArray
  • 避免使用依賴(lài)注入的框架
  • 使用ZIP對(duì)齊的APK
  • 使用多進(jìn)程

七、冷啟動(dòng)優(yōu)化

1、冷啟動(dòng)的定義

冷啟動(dòng)就是在啟動(dòng)應(yīng)用前,系統(tǒng)中沒(méi)有該應(yīng)用的任何進(jìn)程信息

2、冷啟動(dòng)和熱啟動(dòng)的區(qū)別

熱啟動(dòng):用戶(hù)使用返回鍵退出應(yīng)用,然后馬上又重新啟動(dòng)應(yīng)用。熱啟動(dòng)的應(yīng)用的進(jìn)程是保留在后臺(tái)的

  • 冷啟動(dòng):每次啟動(dòng)的時(shí)候都會(huì)走Application這個(gè)類(lèi),
  • 熱啟動(dòng):進(jìn)程中保留留這個(gè)app的進(jìn)行,它會(huì)直接走M(jìn)ainActivity這個(gè)類(lèi)
3、冷啟動(dòng)的流程
  • Zygote進(jìn)程中fork創(chuàng)建出一個(gè)新的進(jìn)程
  • 創(chuàng)建和初始化Application類(lèi)、創(chuàng)建MainActivity類(lèi)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(資源的初始化放到子線(xiàn)程當(dāng)中)

八、其他優(yōu)化問(wèn)題

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問(wèn)題
  • 不能跨進(jìn)程同步
  • 存儲(chǔ)SharePreference的文件過(guò)大問(wèn)題,文件過(guò)大獲取值得時(shí)候,有可能阻塞主線(xiàn)程。解析很大的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)換為可以?xún)?chǔ)存或傳輸?shù)男问降倪^(guò)程

  • Serializeble:Serializeble在序列化的時(shí)候會(huì)產(chǎn)生大量的臨時(shí)變量,從而引起頻繁的垃圾回收。影響UI卡頓,容易引起內(nèi)存抖動(dòng),造成oom
  • Parcelable:不能使用存儲(chǔ)在磁盤(pán)上的文件
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ǔ)在磁盤(pán)上的情況

推薦使用Serializeble進(jìn)行數(shù)據(jù)序列化

避免在UI線(xiàn)程中做繁重的操作

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容