1. Background
Recently, in the iteration of the project version, some small episodes of memory problems appeared, and I spent some time optimizing the memory size of the APP runtime. I will give a summary and share with you.
2. Introduction
In Android program development, when an object no longer needs to be used and should be recycled, and another object in use holds its reference, which causes it to be recycled, which causes the object that should be recycled to be recycled and stays in heap memory, and a memory leak occurs. What are the effects of memory leaks? It is one of the main reasons for application OOM. Since the Android system allocates limited memory for each application, when there are many memory leaks in an application, it will inevitably cause the memory required by the application to exceed the memory limit allocated by the system, which causes memory overflow and leads to application Crash. After understanding the causes and impacts of memory leaks, what we need to do is to master common memory leaks and try to avoid them in future Android program development.
1. Memory leak caused by singletons
Android's singleton mode is very popular among developers, but if used inappropriately, it can also cause memory leaks. Because the static nature of a singleton makes the life cycle of a singleton as long as the application life cycle, this means that if an object is no longer needed and the singleton object still holds a reference to the object, then the object will not be recycled normally, which leads to memory leaks.
public class SingleInstance { private static SingleInstance instance; private Context context; private SingleInstance(Context context) { = context; } public synchronized static SingleInstance getInstance(Context context) { if (instance != null) { instance = new SingleInstance(context); } return instance; } }
This is an ordinary singleton pattern. Everyone knows what is the biggest feature of static variables, which is resident memory. That is to say, if your APP’s process is not missing, it will be in memory all the time. When creating this singleton, since a Context needs to be passed in, the length of the life cycle of the Context is crucial: if the Activity is passed in: When the Activity corresponding to this Context exits, since the life cycle of the Context is as long as the Activity (the Activity is indirectly inherited from the Context), its memory will not be recycled when the Activity exits, because the singleton object holds a reference to the Activity.
So the correct singleton should be this pose:
public class SingleInstance { private static SingleInstance instance; private Context context; private SingleInstance(Context context) { = (); } public synchronized static SingleInstance getInstance(Context context) { if (instance != null) { instance = new SingleInstance(context); } return instance; } }
This way, no matter what Context is passed in, it will eventually use the Application's Context, and the singleton's life cycle is as long as the application's, thus preventing memory leaks.
2. Memory leak caused by creating static instances by non-static internal classes (such as internal classes, anonymous internal classes)
Sometimes we may start activities frequently. In order to avoid repeatedly creating the same data resources, this way of writing occurs:
public class MainActivity extends AppCompatActivity { private InnerClass innerClass; @Override protected void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(.activity_main); if(innerClass == null){ innerClass = new InnerClass(); } } class InnerClass{ } }
This creates a singleton of a non-static inner class inside the Activity, and the data of the singleton is used every time the Activity is started. Although the duplicate creation of resources is avoided, this writing method will cause memory leakage, because the non-static inner class will hold references to the external class by default, and the non-static inner class will create a static instance. The life cycle of the instance is as long as the application, which causes the static instance to always hold references to the Activity, resulting in the Activity's memory resources that cannot be recycled normally.
The correct way is: set the inner class as a static inner class or extract the inner class and encapsulate it into a singleton. If you need to use Context, please use ApplicationContext.
3. Memory leak caused by Handler
The memory leak problem caused by the use of Handler should be said to be the most common. When handling network tasks or encapsulating some request callbacks, the help of Handler should be handled. If the code of Handler is not standardized, it may cause memory leaks, as shown in the following example:
public class MainActivity extends AppCompatActivity { private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { //... } }; @Override protected void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(.activity_main); loadData(); } private void loadData(){ //...request Message message = (); (message); } }
This way of creating a Handler will cause memory leaks. Since mHandler is an instance of the non-static anonymous internal class of the Handler, it holds a reference to the external class Activity. We know that the message queue is constantly polling and processing messages in a Looper thread. When this Activity exits, there are still unprocessed messages in the message queue or are processing messages. The Message in the message queue holds a reference to the mHandler instance, and mHandler holds a reference to the Activity, which causes the memory resources of the Activity to be unable to be recycled in time, causing a memory leak. Therefore, another method is to use soft references:
public class MainActivity extends AppCompatActivity { private MyHandler mHandler = new MyHandler(this); private TextView mTextView ; private static class MyHandler extends Handler { private WeakReference<Context> reference; public MyHandler(Context context) { reference = new WeakReference<>(context); } @Override public void handleMessage(Message msg) { MainActivity activity = (MainActivity) (); if(activity != null){ (""); } } } @Override protected void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(.activity_main); mTextView = (TextView)findViewById(); loadData(); } private void loadData() { //...request Message message = (); (message); } }
Create a static Handler inner class and use weak references to the objects held by the Handler. In this way, objects held by the Handler can also be recycled during recycling. Although the activity leakage is avoided, there may still be pending messages in the message queue of the Looper thread. Therefore, we should remove the messages in the message queue when Destroy or Stop of the Activity. The more accurate approach is as follows:
public class MainActivity extends AppCompatActivity { private MyHandler mHandler = new MyHandler(this); private TextView mTextView ; private static class MyHandler extends Handler { private WeakReference<Context> reference; public MyHandler(Context context) { reference = new WeakReference<>(context); } @Override public void handleMessage(Message msg) { MainActivity activity = (MainActivity) (); if(activity != null){ (""); } } } @Override protected void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(.activity_main); mTextView = (TextView)findViewById(); loadData(); } //Callback for loading network data private void loadData() { //...request Message message = (); (message); } @Override protected void onDestroy() { (); //Remove message queue and callback (null); } }
Use (null); is to remove all messages and all Runnables in the message queue. Of course, you can also use(); or(); to remove the specified Runnable and Message.
Of course it's simpler, you can do it directly
@Override protected void onDestroy() { (); //Remove message queue and callback (null); }
Test it for a while, it works.
4. Memory leak caused by threads
Memory leaks caused by threads are also common. Everyone may have written the following two examples:
//——————test1 new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { (10000); return null; } }.execute(); //——————test2 new Thread(new Runnable() { @Override public void run() { (10000); } }).start();
The asynchronous tasks and Runnable above are both an anonymous inner class, so they all have an implicit reference to the current Activity. If the task is not completed before the Activity is destroyed, the memory resources of the Activity will be unable to be recycled, causing memory leaks. The correct way is to use static inner classes, as follows:
static class MyAsyncTask extends AsyncTask<Void, Void, Void> { private WeakReference<Context> weakReference; public MyAsyncTask(Context context) { weakReference = new WeakReference<>(context); } @Override protected Void doInBackground(Void... params) { (10000); return null; } @Override protected void onPostExecute(Void aVoid) { (aVoid); MainActivity activity = (MainActivity) (); if (activity != null) { //... } } } static class MyRunnable implements Runnable{ @Override public void run() { (10000); } } //—————— new Thread(new MyRunnable()).start(); new MyAsyncTask(this).execute();
This avoids the leakage of memory resources in the Activity. Of course, when the Activity is destroyed, the corresponding task should also be cancelled to avoid wasting resources when the task is executed in the background.
5. Memory leak caused by unclosed resources
For the use of resources such as BraodcastReceiver, ContentObserver, File, Cursor, Stream, Bitmap, etc., it should be closed or cancelled in time when the Activity is destroyed, otherwise these resources will not be recycled, causing memory leakage.
6. Memory leak caused by WebView, or MapView of Baidu or Gaode Map, you can refer to this article in my article
Memory leak caused by WebView:///article/
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.