android中常見的內存泄漏和解決的方法

android中的內存溢出預計大多數人在寫代碼的時候都出現過,事實上忽然認爲工做一年和工做三年的差異是什麼呢。事實上乾的工做或許都同樣,產品汪看到的結果也都同樣,那差異就是速度和質量了。java

寫在前面的一點兒想法:工做作完了事實上不會的還有很是多,天天都有莫名的危機感,從真正寫代碼的這一年多總認爲本身的學習速度比別人的慢很是多android

內存泄漏是什麼鬼?

  • 當某些對象再也不被程序所使用。但是這些對象仍然被某些對象所引用着。進而致使垃圾收集器不能及時釋放它們。數據庫


    (無效的對象沒有及時回收。致使內存不夠用。導致程序
    出錯)
    來個圖片瞭解一下
    這裏寫圖片描寫敘述markdown

    知道爲何致使內存泄露那就很是好辦了。都是跟對象有關係(就是new出來的 不要想着他會跟你結婚)併發

主要有如下幾方面吧:平時注意一下 全然可以杜絕的app

  • Context
  • 內部類(handler等)
  • Cursor
  • Adapter
  • Bitmap

Context的溢出

來個圖讓你們分分鐘理解一下:
這裏寫圖片描寫敘述async

看到這個圖在稍加思索會不會認爲咱們的工具類 貌似好多都持有了activity,而且工具類仍是static類型。
在細琢磨一下呢。是否是activity的上下文都可以被application替代呢?ide

經驗之談:dialog ,fragment。inflate和啓動activity 的上下文都是activity的。其它的都都可以被application替代。比方數據庫的 服務的 廣播的。都不要再用activity了吧。工具

固然也要酌情處理。post

  • 舉個栗子(太多了根本舉只是來)

1.獲取系統的服務

getSystemService(Context.SENSOR_SERVICE);
    改成            getApplicationContext().getSystemService(Context.SENSOR_SERVICE);

2.弄個數據庫

public class NoteSQLiteOpenHelper extends SQLiteOpenHelper { /** * @param context 上下文 ; * @param name 數據庫的名稱 * @param cursorfactory 遊標工廠null爲默認的。是從第一條開始查詢 * @param version 數據庫的版本從1開始。

*/ public NoteSQLiteOpenHelper(getApplicationContext()) { super(getApplicationContext(), "note123.db", null, 1); } } 事實上這裏用activity的也無所謂但是會出現用着用着就把他弄成靜態的了。那就悲劇了。

其它的就是 千萬不要在 static的工具類裏面 增長activity上下文

內部類的種種問題(感受這個比較多一些呢)

這裏寫圖片描寫敘述

簡單說一下呢:比方 activity 開線程 跳刀handler 彈出dialog。 activity在銷燬了,thread持有activity。而後跳到了handler,handler的對象也存在着了對吧,而後彈出dialog。這個時候呢dialog也持有了activity。但是因爲activity銷燬了因此不彈了。但對象仍是持有者了。

怎麼解決 內部類的問題呢。就是在 依附於的那個類銷燬的時候 本身也銷燬。

就類似於activity銷燬了。什麼thread,dialog。handler全關掉

  • handler的狀況
public class LeakActivity extends Activity {

  /** * 聲明靜態內部類不會持有外部類的隱式引用 */
  private static class MyHandler extends Handler {
    private final WeakReference<SampleActivity> mActivity;

    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference<SampleActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
      SampleActivity activity = mActivity.get();
      if (activity != null) {
        // ...
      }
    }
  }

  private final MyHandler mHandler = new MyHandler(this);

  /** * 這裏的Runnable也是 * 聲明靜態內部類不會持有外部類的隱式引用 */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { /* ... */ }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //10分鐘以後發送消息 
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

    // 退回到上一個Activity
    finish();
  }
}


@Override
public void onDestroy() {
   //簡單粗暴
    mHandler.removeMessages(message);
    mHandler.removeCallbacks(Runnable);
    mHandler.removeCallbacksAndMessages(null);
}

切斷聯繫便可啦。
- dialog
剛纔說的按個 activity都銷燬了。

但是還要在handler中運行dialog。那麼久作個推斷吧

Handler handler = new Handler() {
    public void handleMessage(Message msg) {
     super.handleMessage(msg);
        switch (msg.what) {
        case 1:

            if (!isFinishing()) {
               dialog.show();

            }  
            break;
        default:
            break;
        }  

    }  
};
  • thread 和asynctask的狀況
    在寫這個的時候我就在想我還有必要寫麼。

    我都已經過久遠的沒用這兩個了。

    我都用的是線程池了。
    如下貼一個本身定義的線程池代碼。讀者直接拿走(可以用 線程池+handler 或者 線程池+eventbus,rxbus等等來更新ui),activity銷燬的過後 直接讓線程池關閉便可啦

package com.ihealth.utils; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor.AbortPolicy; import java.util.concurrent.TimeUnit; /** * @author wanghao * * 說明:一個簡易的線程池管理類。提供三個線程池 進本的 子線程操做都可以知足 * * 線程內部都是 挨個進行,僅僅有建立多個線程池的纔可能會併發進行。

*/ public class ThreadManager { public static final String DEFAULT_SINGLE_POOL_NAME = "DEFAULT_SINGLE_POOL_NAME"; private static ThreadPoolProxy mLongPool = null; private static Object mLongLock = new Object(); private static ThreadPoolProxy mShortPool = null; private static Object mShortLock = new Object(); private static ThreadPoolProxy mDownloadPool = null; private static Object mDownloadLock = new Object(); private static Map<String, ThreadPoolProxy> mMap = new HashMap<String, ThreadPoolProxy>(); private static Object mSingleLock = new Object(); /** 獲取下載線程 */ public static ThreadPoolProxy getDownloadPool() { synchronized (mDownloadLock) { if (mDownloadPool == null) { mDownloadPool = new ThreadPoolProxy(3, 3, 5L); } return mDownloadPool; } } /** 獲取一個用於運行長耗時任務的線程池,避免和短耗時任務處在同一個隊列而堵塞了重要的短耗時任務,通常用來聯網操做 */ public static ThreadPoolProxy getLongPool() { synchronized (mLongLock) { if (mLongPool == null) { mLongPool = new ThreadPoolProxy(5, 5, 5L); } return mLongPool; } } /** 獲取一個用於運行短耗時任務的線程池,避免因爲和耗時長的任務處在同一個隊列而長時間得不到運行。通常用來運行本地的IO/SQL */ public static ThreadPoolProxy getShortPool() { synchronized (mShortLock) { if (mShortPool == null) { mShortPool = new ThreadPoolProxy(2, 2, 5L); } return mShortPool; } } /** 獲取一個單線程池,所有任務將會被依照增長的順序運行,免除了同步開銷的問題 */ public static ThreadPoolProxy getSinglePool() { return getSinglePool(DEFAULT_SINGLE_POOL_NAME); } /** 獲取一個單線程池,所有任務將會被依照增長的順序運行。免除了同步開銷的問題 */ public static ThreadPoolProxy getSinglePool(String name) { synchronized (mSingleLock) { ThreadPoolProxy singlePool = mMap.get(name); if (singlePool == null) { singlePool = new ThreadPoolProxy(1, 1, 5L); mMap.put(name, singlePool); } return singlePool; } } public static class ThreadPoolProxy { private ThreadPoolExecutor mPool; private int mCorePoolSize; private int mMaximumPoolSize; private long mKeepAliveTime; private ThreadPoolProxy(int corePoolSize, int maximumPoolSize, long keepAliveTime) { mCorePoolSize = corePoolSize; mMaximumPoolSize = maximumPoolSize; mKeepAliveTime = keepAliveTime; } /** 運行任務,當線程池處於關閉,將會又一次建立新的線程池 */ public synchronized void execute(Runnable run) { if (run == null) { return; } if (mPool == null || mPool.isShutdown()) { //參數說明 //當線程池中的線程小於mCorePoolSize。直接建立新的線程增長線程池運行任務 //當線程池中的線程數目等於mCorePoolSize,將會把任務放入任務隊列BlockingQueue中 //當BlockingQueue中的任務放滿了,將會建立新的線程去運行。 //但是當總線程數大於mMaximumPoolSize時,將會拋出異常,交給RejectedExecutionHandler處理 //mKeepAliveTime是線程運行完任務後,且隊列中沒有可以運行的任務,存活的時間,後面的參數是時間單位 //ThreadFactory是每次建立新的線程工廠 mPool = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, mKeepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(), new AbortPolicy()); } mPool.execute(run); } /** 取消線程池中某個還未運行的任務 */ public synchronized void cancel(Runnable run) { if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { mPool.getQueue().remove(run); } } /** 取消線程池中某個還未運行的任務 */ public synchronized boolean contains(Runnable run) { if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { return mPool.getQueue().contains(run); } else { return false; } } /** 立馬關閉線程池。而且正在運行的任務也將會被中斷 */ public void stop() { if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { mPool.shutdownNow(); } } /** 平緩關閉單任務線程池,但是會確保所有已經增長的任務都將會被運行完成才關閉 */ public synchronized void shutdown() { if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) { mPool.shutdownNow(); } } } }
  • 廣播
    … has leaked IntentReceiver … Are you missing a call to unregisterReceiver()?
    這個錯誤咱們現在公司的代碼還有大把的了。

    這麼解決,一個大神的方法

簡單粗暴
private void unregisterReceiverSafe(BroadcastReceiver receiver) {
    try {
        getContext().unregisterReceiver(receiver);
    } catch (IllegalArgumentException e) {
        // ignore
    }
}

接下來的幾個感受你們應該都不會出太大的問題吧

Cursor

try{}catch(){}
finally{ cur.close(); cur=null; }

adapter (現在都用recycleview了。感受寫這個有點兒雞肋)

會形成內存溢出代碼:
public View getView  (int position, View convertView, ViewGroup parent)  {
          View  view   =   new  View();
          XXX                   XXX
          return    view;
}
修正後的代碼:
public View getView (int position, View convertView, ViewGroup parent)  {
           if  (convertView  ==  null)  {
                 convertView   =  new View();
                 XXX                     XXX
           }  else  {
                 XXX                     XXX
           }
}

Bitmap

不用的時候調用 recycle(),把他清理掉
也可以用lrucache等方法,這就不作具體介紹。

轉載請註明:http://blog.csdn.net/wanghao200906/article/details/50426881
就這樣吧。忙裏偷閒寫了個博客,總結一下,事實上內存溢出的問題都是 不當心致使的,避免起來也比較easy。

文章有點兒長 讀完了 :辛苦了您內,0基礎文章大神勿噴

相關文章
相關標籤/搜索