今天在測一個(gè)新項(xiàng)目,報(bào)了一個(gè)錯(cuò)誤:
java.lang.OutOfMemoryError,
Failed to allocate a 2822652 byte allocation with 284176 free bytes and 277KB until OOM
機(jī)型跟參數(shù)
HMNOTE2_V8.5.2.0
其實(shí)這個(gè)異常,只會在加載大量圖片,數(shù)據(jù)的時(shí)候出現(xiàn)?;蛘咦约簩懥颂幚聿划?dāng)?shù)姆椒ā?/p>
而我內(nèi)存溢出的問題是因?yàn)椋篐andler內(nèi)存泄露
原因是:我在延時(shí)處理一個(gè)事件用了Handler(即啟動頁延時(shí)2分鐘進(jìn)入App),但是acitivity關(guān)閉后這個(gè)handler還在執(zhí)行。
Handler 的生命周期與Activity 不一致:
當(dāng)Android應(yīng)用啟動的時(shí)候,會先創(chuàng)建一個(gè)UI主線程的Looper對象,Looper實(shí)現(xiàn)了一個(gè)簡單的消息隊(duì)列,一個(gè)一個(gè)的處理里面的Message對象。主線程Looper對象在整個(gè)應(yīng)用生命周期中存在。
當(dāng)在主線程中初始化Handler時(shí),該Handler和Looper的消息隊(duì)列關(guān)聯(lián)(沒有關(guān)聯(lián)會報(bào)錯(cuò)的)。發(fā)送到消息隊(duì)列的Message會引用發(fā)送該消息的Handler對象,這樣系統(tǒng)可以調(diào)用 Handler#handleMessage(Message) 來分發(fā)處理該消息。
handler 引用 Activity 阻止了GC對Acivity的回收
在Java中,非靜態(tài)(匿名)內(nèi)部類會默認(rèn)隱性引用外部類對象。而靜態(tài)內(nèi)部類不會引用外部類對象。
如果外部類是Activity,則會引起Activity泄露 。
當(dāng)Activity finish后,延時(shí)消息會繼續(xù)存在主線程消息隊(duì)列中1分鐘,然后處理消息。而該消息引用了Activity的Handler對象,然后這個(gè)Handler又引用了這個(gè)Activity。這些引用對象會保持到該消息被處理完,這樣就導(dǎo)致該Activity對象無法被回收,從而導(dǎo)致了上面說的 Activity泄露。
解決辦法如下:
private class MyHandler extends Handler {
private WeakReference<HandlerActivity> mActivity;
public MyHandler(HandlerActivity activity) {
mActivity = new WeakReference<HandlerActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
if (mActivity.get() == null) {
return;
}
mActivity.get().todo();
}
}
@Override
public void onDestroy() {
if(mHandler!=null){
mHandler.removeCallbacksAndMessages(null);
}
}
//以下是調(diào)用的地方
mHandler = new MyHandler(HandlerActivity.this);
mHandler.sendMessageDelayed(Message.obtain(), 1000);
另外這是網(wǎng)上可能解決辦法:
1、圖片建議都放在mipmap目錄下。
2、申請更多的空間。
但并不是所有項(xiàng)目都應(yīng)該使用這樣的方法去解決OutOfMemoryError問題,如果是大圖片和視頻應(yīng)用可以使用android:largeHeap="true";
android:hardwareAccelerated="false",
android:largeHeap="true"
Android提供了在以下各級別上啟用或禁止硬加速的能力:
- Application
- Activity
- Window
- View
如下
//清單文件
<applicationandroid:hardwareAccelerated="true">
<activity... />
<activityandroid:hardwareAccelerated="false" />
</application>
//動態(tài)代碼
myView.setLayerType(View.LAYER_TYPE_SOFTWARE,null);//禁止個(gè)別的View的硬加速
//檢查應(yīng)用是否被硬加速
View.isHardwareAccelerated():如果View附加到一個(gè)硬加速的window上就返回true.
Canvas.isHardwareAccelerated():如果Canvas被硬加速了就返回true.
3、 避免在 activity 或 fragment 之外傳遞 Context 對象。
4、 永遠(yuǎn)永遠(yuǎn)不要創(chuàng)建靜態(tài)的 Context 或 View 對象,或者將二者存儲于靜態(tài)變量中。這是內(nèi)存泄露的首要標(biāo)志。
private static TextView textView;
private static Context context;
5、 總是記得在 onPause() 或 onDestroy() 方法中的取消注冊監(jiān)聽器(listeners)。這包括 Android 監(jiān)聽器,以及位置服務(wù)、顯示管理器服務(wù),還有自定義的一些監(jiān)聽器。
6、 不要在 AsyncTasks(異步任務(wù))或后臺線程中存儲指向 activities 的強(qiáng)引用。Activity 可能會關(guān)閉,但是 AsyncTask 會繼續(xù)執(zhí)行,一直保存著對該 activity 的引用。
7、 盡力避免使用非靜態(tài)的內(nèi)部類。將引用存儲至某個(gè) Activity 或 View 內(nèi)部會導(dǎo)致內(nèi)存泄露。如果不得不存儲引用,請使用WeakReference。
參考鏈接
內(nèi)存分析器
堆內(nèi)存查看器介紹
內(nèi)存監(jiān)控器
在 Android 中避免內(nèi)存泄露