Android异常与性能优化相关问题及解决办法
目錄
- ANR異常問題及解決方式
- OOM異常問題及解決方式
- BitMap相關(guān)問題
- UI卡頓問題
- 內(nèi)存泄露問題及解決方式
- 內(nèi)存管理
- 冷啟動優(yōu)化問題
- 其他優(yōu)化介紹
ANR異常問題及解決方式
什么是ANR
ANR全名Application Not Responding, 也就是”應(yīng)用無響應(yīng)”. 當(dāng)操作在一段時間內(nèi)系統(tǒng)無法處理時, 系統(tǒng)層面會彈出上圖那樣的ANR對話框.
造成ANR的主要原因
在Android里, App的響應(yīng)能力是由Activity Manager和Window Manager系統(tǒng)服務(wù)來監(jiān)控的. 通常在如下兩種情況下會彈出ANR對話框:
- 5s內(nèi)無法響應(yīng)用戶輸入事件(例如鍵盤輸入, 觸摸屏幕等).
- BroadcastReceiver在10s內(nèi)無法結(jié)束.
造成以上兩種情況的首要原因就是在主線程(UI線程)里面做了太多的阻塞耗時操作, 例如文件讀寫, 數(shù)據(jù)庫讀寫, 網(wǎng)絡(luò)查詢等等.
那么Android中哪些操作是在主線程尼?
- Activity的所有生命周期都是執(zhí)行在主線程
- Service默認是執(zhí)行在主線程
- BroadcastReceiver的onReceiver回調(diào)是執(zhí)行在主線程的
- 沒有使用子線程的looper的Handler的handleMessage,post(Runnnable)是執(zhí)行在主線程的。
- AsyncTask的回調(diào)中除了donInbackground,其他都在主線程中執(zhí)行。
如何解決ANR
- 使用AsyncTask處理耗時操作
- 使用Thread或者HandlerThread提高優(yōu)先級
- 使用handler來處理工作線程的耗時操作
- Activity的onCreate和onResume回調(diào)中盡量避免耗時的代碼
OOM異常問題及解決方式
什么是OOM?
當(dāng)前占用的內(nèi)存加上我們申請的內(nèi)存資源超過了Dalvik虛擬機的最大內(nèi)存限制就會拋出的Out of memory 異常。
一些容易混淆的概念
內(nèi)存溢出:即是OOM
內(nèi)存抖動:因為短時間內(nèi)創(chuàng)建大量的對象,然后瞬間釋放,這段時間占有的內(nèi)存區(qū)域,會產(chǎn)生抖動。
內(nèi)存泄露:產(chǎn)生內(nèi)存垃圾,無法被GC回收。
如何解決OOM
產(chǎn)生OOM有以下方式:
- 1.數(shù)據(jù)庫的cursor沒有關(guān)閉。
- 2.構(gòu)造adapter沒有使用緩存contentview。
- 3.調(diào)用registerReceiver()后未調(diào)用unregisterReceiver().
- 4.未關(guān)閉InputStream/OutputStream。
- 5.Bitmap使用后未調(diào)用recycle(),控制圖像縮放大小,使用inBitmap屬性,三級緩存
- 6.Context泄漏。
- 7.static關(guān)鍵字等。
- 8.避免在onDraw方法里執(zhí)行對象的創(chuàng)建
- 9.謹慎使用多進程
BitMap相關(guān)問題
- 1.recycle
- 2.LRU
- 3.計算inSampleSize,使用縮略圖
- 4.三級緩存
- 5.軟引用
雖然,系統(tǒng)能夠確認Bitmap分配的內(nèi)存最終會被銷毀,但是由于它占用的內(nèi)存過多,所以很可能會超過java堆的限制。因此,在用完Bitmap時,要 及時的recycle掉。recycle并不能確定立即就會將Bitmap釋放掉,但是會給虛擬機一個暗示:“該圖片可以釋放了”, 還有就是, 雖然recycle()從源碼上看,調(diào)用它應(yīng)該能立即釋放Bitmap的主要內(nèi)存,但是測試表明它并沒能立即釋放內(nèi)存。故我們還需手動設(shè)置為NULL這樣 還能大大的加速Bitmap的主要內(nèi)存的釋放。。
如下:
對于LRU 請移步我的另一篇博客 Android中LruCache案例及實現(xiàn)原理分析
有時候,我們要顯示的區(qū)域很小,沒有必要將整個圖片都加載出來,而只需要記載一個縮小過的圖片,這時候可以設(shè)置一定的采樣率,那么就可以大大減小占用的內(nèi)存。如下面的代碼:
/*** Return the sample size.** @param options The options.* @param maxWidth The maximum width.* @param maxHeight The maximum height.* @return the sample size*/private static int calculateInSampleSize(final BitmapFactory.Options options,final int maxWidth,final int maxHeight) {int height = options.outHeight;int width = options.outWidth;int inSampleSize = 1;while ((width >>= 1) >= maxWidth && (height >>= 1) >= maxHeight) {inSampleSize <<= 1;}return inSampleSize;}private static boolean isEmptyBitmap(final Bitmap src) {return src == null || src.getWidth() == 0 || src.getHeight() == 0;}/*** Return the compressed bitmap using sample size.** @param src The source of bitmap.* @param maxWidth The maximum width.* @param maxHeight The maximum height.* @param recycle True to recycle the source of bitmap, false otherwise.* @return the compressed bitmap*/public static Bitmap compressBySampleSize(final Bitmap src,final int maxWidth,final int maxHeight,final boolean recycle) {if (isEmptyBitmap(src)) return null;BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;ByteArrayOutputStream baos = new ByteArrayOutputStream();src.compress(Bitmap.CompressFormat.JPEG, 100, baos);byte[] bytes = baos.toByteArray();BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);options.inJustDecodeBounds = false;if (recycle && !src.isRecycled()) src.recycle();return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); }附上關(guān)于BitMap工具類下載地址
巧妙的運用軟引用(SoftRefrence)
有些時候,我們使用Bitmap后沒有保留對它的引用,因此就無法調(diào)用Recycle函數(shù)。這時候巧妙的運用軟引用,可以使Bitmap在內(nèi)存快不足時得到有效的釋放。如下例:
通過使用三級緩存請移步我的另一篇博客 Android中三級緩存實現(xiàn)原理及案例分析
UI卡頓問題
UI卡頓原理
不卡頓情況要60幀操作需要在16ms時間完成,如果完成不了,那么就會造成卡頓現(xiàn)象,渲染失敗,還有一種情況是overDraw情況,一般是重疊View,非必要重疊背景
UI卡頓原因分析
1、人為在UI線程中做輕微耗時操作,導(dǎo)致UI線程卡頓
2、布局Layout過于復(fù)雜,無法在16ms內(nèi)存完成渲染
3、同一時間動畫執(zhí)行的次數(shù)過多,導(dǎo)致CPU或GPU負載過重
4、View過度繪制,導(dǎo)致某些像素在同一幀時間內(nèi)被繪制多次,從而使CPU或GPU負載過重
5、View頻繁的觸發(fā)measure、layout,導(dǎo)致measure、layout累計耗時過多及整個View頻繁的重新渲染
6、內(nèi)存頻繁觸發(fā)GC過多,導(dǎo)致暫時阻塞渲染操作
7、冗余資源及邏輯等導(dǎo)致加載和執(zhí)行緩慢
8、ANR
UI卡頓總結(jié)
1、布局優(yōu)化:不使用太多嵌套,使用weight 代替長和寬的計算,使用Gone代替invisible
2、列表及Adapter優(yōu)化
3、背景和圖片等內(nèi)存分配優(yōu)化
4、避免ANR
內(nèi)存泄露問題及解決方式
java內(nèi)存泄露基礎(chǔ)知識
1、java內(nèi)存的分配策略
寄存器:我們在程序中無法控制
棧:存放基本類型的數(shù)據(jù)和對象的引用,但對象本身不存放在棧中,而是存放在堆中
堆:存放用new產(chǎn)生的數(shù)據(jù)
靜態(tài)域:存放在對象中用static定義的靜態(tài)成員
常量池:存放常量
非RAM(隨機存取存儲器)存儲:硬盤等永久存儲空間
2、java是如何管理內(nèi)存的
java管理內(nèi)存實際上就是對象的生成與釋放的管理,對象的生成是在堆中生成的,釋放是靠GC回收機制,前者是程序員控制,后者靠java虛擬機控制,下面是一個案例:
3、java中的內(nèi)存泄露
內(nèi)存泄露是指無用對象(不再使用的對象)持續(xù)占有內(nèi)存或無用對象的內(nèi)存得不到及時釋放,從而造成的內(nèi)存空間的浪費稱為內(nèi)存泄露
- 單例引起的內(nèi)存泄漏
使與應(yīng)用生命周期一樣長,持有該對象的引用,
內(nèi)存泄露問題:
public class AppManager{private static AppManager instance;private Context mcontext;private AppManager(){}private AppManager(Context context){mcontext=context;}private static AppManager getInstance(Context context){if(instance==null){instance=new AppManager(context);}return instance;}} public class AppManager{private static AppManager instance;private Context mcontext;private AppManager(){}private AppManager(Context context){mcontext=context.getApplicationContext();//使用Application的context}private static AppManager getInstance(Context context){if(instance==null){instance=new AppManager(context);}return instance;} }- 匿名內(nèi)部類
非靜態(tài)內(nèi)部類持有外部類的引用,而使用非靜態(tài)內(nèi)部類創(chuàng)建一個靜態(tài)實例。正確解決辦法是將該類變?yōu)殪o態(tài)類
- handler
原因:非靜態(tài)內(nèi)部類持有外部類的引用。
handler造成的內(nèi)存泄漏解決辦法:
private MyHandler mHandler = new MyHandler(this); private TextView mTextView; private static class MyHandler extends Handler{private WeekReference<Context> reference;public MyHandler(Context context){reference=new WeekReference<>(context);}@Overridepublic void handleMessage(Message msg){MainActivity activity=(MainActivity)reference.get();if(activity!=null){activity.mTextview.setText("");}}}- 避免使用static變量
- 資源未關(guān)閉造成的內(nèi)存泄露。
- AsyncTask造成的內(nèi)存泄露
原因:非靜態(tài)內(nèi)部類持有外部類引用;
解決辦法:
在Activity的onDestory方法中調(diào)用AsyncTask的cancel方法。
4、內(nèi)存優(yōu)化方法
- 當(dāng)Service完成任務(wù)后,盡量停止它;
- 在UI不可見的時候,釋放掉一些只有UI使用的資源;
- 在系統(tǒng)內(nèi)存緊張的時候,盡可能多的釋放掉一些非重要的資源;
- 避免濫用BitMap導(dǎo)致的內(nèi)存浪費
- 使用針對內(nèi)存優(yōu)化過的數(shù)據(jù)容器 SpareArray代替HashMap 枚舉類型常量是一般常量類型占用內(nèi)存的兩倍
- 避免使用依賴注入的框架
- 使用ZIP對齊的APK
- 使用多進程,比如通過開啟進程 解決開啟定位,開啟推送,開啟webview加載url
冷啟動優(yōu)化
冷啟動的定義及與熱啟動的區(qū)別
冷啟動:就是在啟動應(yīng)用前,系統(tǒng)中沒有該應(yīng)用的任何進程信息。會初始化Application類
熱啟動:用戶使用返回鍵退出應(yīng)用,然后馬上又重新啟動應(yīng)用。不會初始化Application類
冷啟動的時間計算:
這個時間從應(yīng)用啟動(創(chuàng)建進程)開始計算,到完成視圖的第一次繪制(即Activity內(nèi)容對用戶可見)為止。
冷啟動流程:
1、Zygote進程中fork創(chuàng)建出一個新的進程,
2、創(chuàng)建和初始化Application類、創(chuàng)建MainActivity類,inflate布局;
3、當(dāng)onCreate/onStart/onResume方法走完,contentView的measure/layout/draw顯示在桌面上
冷啟動流程-總結(jié):
Application的構(gòu)造方法–>attachBaseContext()—>onCreate()–>Activity的構(gòu)造方法–>onCreate()–>配置主題中背景等屬性–>onStart()–>onResume()–>測量布局繪制顯示在桌面上。
如何對冷啟動的時間進行優(yōu)化
1、減少onCreate()方法的工作量
2、不讓Application參與業(yè)務(wù)的操作
3、不要在Application進行耗時操作
4、不要以靜態(tài)變量的方式在Application中保存數(shù)據(jù)
5、布局/mainThread
其他優(yōu)化:
- android不要靜態(tài)變量存儲數(shù)據(jù)
1、靜態(tài)變量等數(shù)據(jù)由于進程已經(jīng)被殺死而被初始化
2、使用其他數(shù)據(jù)傳輸方式:文件/sp/contentProvider
- 有關(guān)Sharepreference問題
1、不能跨進程同步數(shù)據(jù)讀寫操作
2、存儲Sharepreference的文件過大問題,可能引起ui卡頓,
- 內(nèi)存對象序列化
1、實現(xiàn)Serializeble接口
2、Parcelable (不適用磁盤數(shù)據(jù)序列化)
總結(jié):Serializeble是java的序列化方式,Parcelable是Android特有的序列化方式,在使用內(nèi)存的時候,Parcelable比Serializable性能高
Serializable在序列化的時候會產(chǎn)生大量的臨時變量,從而引起頻繁的GC操作,Parcelable不能使用在要將數(shù)據(jù)存貯在磁盤的情況
總結(jié)
以上是生活随笔為你收集整理的Android异常与性能优化相关问题及解决办法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Cent OS7
- 下一篇: 在Android手机上将Minecraf