java.lang.OutOfMemoryError, Failed to allocate a 2822652 byte allocation with 284176 free bytes and 277KB until OOM

今天在測一個(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)存泄露

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

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

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