Common memory overflows and solutions in Android

Memory overflow in android is estimated that most people have been writing code before. What is the difference between a year's work and a three-year job? Actually, the work may be the same, and the product may see the same results. The difference is speed and quality.

Write a little thought before: there are many more things that won't be done, there is an unknown sense of crisis every day, from the year when you really write code, you always feel that your learning speed is much slower than others

What is a memory overflow?

  • When some objects are no longer used by the program, they are still referenced by some objects, which prevents the garbage collector from releasing them in time.(
    (Invalid objects are not recycled in time, resulting in insufficient memory and causing the program to
    Error)
    Take a picture to find out.

    It's great to know why it caused a memory leak. It's all about the person you're new to (just don't think he'll marry you)

There are the following main aspects: In peacetime, pay attention to completely erasable

  • Context
  • Internal classes (handler, etc.)
  • Cursor
  • Adapter
  • Bitmap

Overflow of Context

Let's take a minute to understand:

Looking at this diagram for a moment, you might wonder if many of our tool classes seem to have activities, and they are still static.(
Think it over.Can the context of an activity be replaced by an application?

Experience: The contexts of dialog, fragment, inflate and activation are active, and everything else can be replaced by application.Such as the broadcasting of a database service.Don't use activity anymore.Of course, it should also be handled as appropriate.

  • Raise a chestnut (too many to lift at all)

1. Get the system's services

getSystemService(Context.SENSOR_SERVICE);
    //Change to getApplicationContext().getSystemService(Context.SENSOR_SERVICE);

2. Get a database

public class NoteSQLiteOpenHelper extends SQLiteOpenHelper {

    /**
     * @param context context;
     * @param name database name
     * @param cursorfactory cursor factory null is the default and queries start from the first
     * @param version database version number starts from 1.
     */

    public NoteSQLiteOpenHelper(getApplicationContext()) {

        super(getApplicationContext(), "note123.db", null, 1);
    }
}
Actually, it doesn't matter what you use activity here, but it will make him static if you use it. That's tragic.

The other is never to add an activity context to a static's tool class

All sorts of internal problems (feel this is a little more)

Simply put it this way: activity opens the thread and jumps handler to pop up dialog.The activity is destroyed, threads hold the activity, then jump to the handler, the handler object exists, right, and then pop up the dialog.At this point, dialog also holds the activity, but because the activity is destroyed, it does not bounce.But the object is still the holder.

How to solve the problem of internal classes.It is at the time of the destruction of the attached type that it destroys itself.It's like an activity destroyed.What thread, dialog, handler all off

  • handler's case
public class LeakActivity extends Activity {

  /**
   * Declare that static internal classes do not hold implicit references to external classes
   */
  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);

  /**
   * So is Runnable here 
   * Declare that static internal classes do not hold implicit references to external classes
   */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { /* ... */ }
  };

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

    //Send a message 10 minutes later 
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

    // Back to previous Activity
    finish();
  }
}


@Override
public void onDestroy() {
   //Simple and rough
    mHandler.removeMessages(message);
    mHandler.removeCallbacks(Runnable);
    mHandler.removeCallbacksAndMessages(null);
}

Just disconnect.(
- dialog 
Every single activity you just said is destroyed.But you still have to execute dialog in handler, so make a decision

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

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

            }  
            break;
        default:
            break;
        }  

    }  
};
  • Threads and asynctask s
    As I write this, I wonder if I need to write more.I haven't used both for too long.I'm using thread pools.(
    Following is a custom thread pool code that the reader can take away directly (the ui can be updated with thread pool + handler or thread pool + eventbus, rxbus, etc.). Let the thread pool close directly after the activity is destroyed
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
 * 
 * Description: A simple thread pool management class that provides three thread pool cost-entry sub-threading operations
 * 
 * Threads are run one by one, and only those that create multiple thread pools may run concurrently.
 */
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();

    /** Get Download Thread */
    public static ThreadPoolProxy getDownloadPool() {
        synchronized (mDownloadLock) {
            if (mDownloadPool == null) {
                mDownloadPool = new ThreadPoolProxy(3, 3, 5L);
            }
            return mDownloadPool;
        }
    }

    /** Gets a thread pool for long and short time-consuming tasks, avoids being in the same queue as short time-consuming tasks and blocks important short time-consuming tasks, commonly used for networking operations */
    public static ThreadPoolProxy getLongPool() {
        synchronized (mLongLock) {
            if (mLongPool == null) {
                mLongPool = new ThreadPoolProxy(5, 5, 5L);
            }
            return mLongPool;
        }
    }

    /** Gets a thread pool for short time consuming tasks, avoiding long periods of non-execution because they are in the same queue as long time consuming tasks, usually for local IO/SQL execution */
    public static ThreadPoolProxy getShortPool() {
        synchronized (mShortLock) {
            if (mShortPool == null) {
                mShortPool = new ThreadPoolProxy(2, 2, 5L);
            }
            return mShortPool;
        }
    }

    /** Get a single-threaded pool and all tasks will be executed in the order they were joined, eliminating synchronization overhead */
    public static ThreadPoolProxy getSinglePool() {
        return getSinglePool(DEFAULT_SINGLE_POOL_NAME);
    }

    /** Get a single-threaded pool and all tasks will be executed in the order they were joined, eliminating synchronization overhead */
    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;
        }

        /** Execute the task, when the thread pool is closed, a new thread pool will be recreated */
        public synchronized void execute(Runnable run) {
            if (run == null) {
                return;
            }
            if (mPool == null || mPool.isShutdown()) {
                //Parameter Description
                //When the thread in the thread pool is smaller than mCorePoolSize, create a new thread directly to join the thread pool to perform tasks
                //When the number of threads in the thread pool equals mCorePoolSize, the task is placed in the task queue BlockingQueue
                //When the BlockingQueue is full, a new thread is created to execute.
                //However, when the number of bus threads is greater than mMaximumPoolSize, an exception is thrown and handled by the RejectedExecutionHandler
                //mKeepAliveTime is the time to live when a thread has finished executing a task and there are no tasks in the queue to execute. The latter parameter is the time unit
                //ThreadFactory is a new thread factory created every time
                mPool = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, mKeepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(), new AbortPolicy());
            }
            mPool.execute(run);
        }

        /** Cancel an unfinished task in the thread pool */
        public synchronized void cancel(Runnable run) {
            if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) {
                mPool.getQueue().remove(run);
            }
        }

        /** Cancel an unfinished task in the thread pool */
        public synchronized boolean contains(Runnable run) {
            if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) {
                return mPool.getQueue().contains(run);
            } else {
                return false;
            }
        }

        /** Close the thread pool immediately and the task being performed will also be interrupted */
        public void stop() {
            if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) {
                mPool.shutdownNow();
            }
        }

        /** Gently closes the single-task thread pool, but ensures that all joined tasks will be executed before closing */
        public synchronized void shutdown() {
            if (mPool != null && (!mPool.isShutdown() || mPool.isTerminating())) {
                mPool.shutdownNow();
            }
        }
    }
}
  • Broadcast
    ... has leaked IntentReceiver ... Are you missing a call to unregisterReceiver()? 
    This error we now have a lot of company code.The solution, a great god's way
Simple and rough
private void unregisterReceiverSafe(BroadcastReceiver receiver) {
    try {
        getContext().unregisterReceiver(receiver);
    } catch (IllegalArgumentException e) {
        // ignore
    }
}

The next few feelings are that you should not have too many problems.

Cursor

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

adapter (recycleview is now used.Feel like writing this with a little chicken ribs)

Causes memory overflow code:
public View getView  (int position, View convertView, ViewGroup parent)  {
          View  view   =   new  View();
          XXX                   XXX
          return    view;
}
//Corrected code:
public View getView (int position, View convertView, ViewGroup parent)  {
           if  (convertView  ==  null)  {
                 convertView   =  new View();
                 XXX                     XXX
           }  else  {
                 XXX                     XXX
           }
}

Bitmap

Call recycle when not in use to clean it up.
You can also use methods such as lrucache, which are not described in detail.

Reference article: http://blog.csdn.net/wanghao200906/article/details/50426881 

Keywords: Java Database Android Fragment

Added by quimbley on Tue, 21 May 2019 20:42:42 +0300