English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
In Android, an object is no longer needed, but other objects still hold its reference, causing it not to be recycled, resulting in the object temporarily existing in memory, and thus memory leaks occur.
When memory leaks occur frequently, the application will occupy too much memory. When the occupied memory exceeds the memory capacity allocated by the system, a memory overflow will occur, leading to the application crash.
After understanding the causes and effects of memory leaks, what we need to do is master the common memory leaks and try to avoid them in the future Android program development. The following collects5A common memory leak problem in Android development and its solutions are shared with everyone. Let's take a look together.
1.Memory leaks caused by singletons
Everyone likes to use Android's singleton. However, the static feature of the singleton pattern makes its lifecycle the same as that of the application, which means that when an object is no longer needed, the singleton object still holds a reference to it, and this object cannot be released, leading to memory leaks.
Example:
public class AppManager { private static AppManager instance; private Context context; private AppManager(Context context) { this.context = context; } public static AppManager getInstance(Context context) { if (instance != null) { instance = new AppManager(context); } return instance; } }
The lifecycle of the Context object passed into this singleton is crucial because this singleton needs to pass in a Context object:
12. The Context passed in is the Application's Context: This will not cause any problems because the lifecycle of the singleton is the same as that of the Application;
21. The Context passed in is the Activity's Context: When the corresponding Activity exits, since the Context and Activity have the same lifecycle (Activity indirectly inherits from Context), the memory of the current Activity will not be recycled because the singleton object holds a reference to the Activity.
So the correct singleton should be modified to the following method:
public class AppManager { private static AppManager instance; private Context context; private AppManager(Context context) { this.context = context.getApplicationContext(); } public static AppManager getInstance(Context context) { if (instance != null) { instance = new AppManager(context); } return instance; } }
In this way, no matter what Context is passed in, the Application's Context will be used in the end, and the lifetime of the singleton is the same as that of the application, thus preventing memory leaks.
2. Memory leaks caused by creating a static instance of a non-static inner class
Sometimes, in order to avoid the repeated creation of the same data resources in frequently started Activities, this kind of writing may appear:
public class MainActivity extends AppCompatActivity { private static TestResource mResource = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(mManager == null){ mManager = new TestResource(); } //... } class TestResource { //... } }
In this way, a non-static inner class singleton is created within the Activity. Each time the Activity is started, it will use the data of this singleton. Although this avoids the repeated creation of resources, this writing style will cause memory leaks because the non-static inner class holds a reference to the external class by default, and a static instance is created using this non-static inner class. The lifetime of this static instance is the same as that of the application, which leads to this static instance always holding a reference to the Activity, causing the memory resources of the Activity cannot be normally recycled. The correct approach is to:
Set the inner class to a static inner class or extract it and encapsulate it into a singleton. If you need to use Context, please use ApplicationContext.
3. Memory leaks caused by Handler
Memory leaks caused by the use of Handler should be the most common problem, as we often use Handler to handle network tasks or encapsulate some request callbacks and other APIs in daily use. Inconsistent coding of Handler usage 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) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); loadData(); } private void loadData() { //...request Message message = Message.obtain(); mHandler.sendMessage(message); } }
This way of creating a Handler will cause memory leaks, as mHandler is an instance of a non-static anonymous inner class of Handler, which holds a reference to the external class Activity. We know that the message queue is constantly polling and processing messages in a Looper thread, so when this Activity exits, there may still be unprocessed messages or messages being processed in the message queue. The Message in the message queue holds a reference to the mHandler instance, and mHandler holds a reference to the Activity, which leads to the memory resources of this Activity not being recycled in time, causing memory leaks. Therefore, another approach is to:
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) reference.get(); if(activity != null){ activity.mTextView.setText(""); } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView)findViewById(R.id.textview); loadData(); } private void loadData() { //...request Message message = Message.obtain(); mHandler.sendMessage(message); } }
Create a static Handler inner class, and then use a weak reference to the object held by the Handler, so that the object held by the Handler can also be recycled when it is recycled. Although this avoids the leakage of Activity, 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 the Activity is destroyed or stopped. A 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) reference.get(); if(activity != null){ activity.mTextView.setText(""); } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView)findViewById(R.id.textview); loadData(); } private void loadData() { //...request Message message = Message.obtain(); mHandler.sendMessage(message); } @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); } }
Using mHandler.removeCallbacksAndMessages(null); is to remove all messages and all Runnables from the message queue. Of course, you can also use mHandler.removeCallbacks(); or mHandler.removeMessages(); to remove specific Runnables and Messages.
IV. Memory leaks caused by unclosed resources
For resources such as BraodcastReceiver, ContentObserver, File, Cursor, Stream, Bitmap, etc., they should be closed or unregistered in time when the Activity is destroyed, otherwise these resources will not be recycled, causing memory leaks.
V. Memory leaks caused by threads
Memory leaks caused by threads are also quite common in daily use, as shown in the following two examples that many people may have written:
//——————test1 new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { SystemClock.sleep(10000); return null; } }.execute(); //——————test2 new Thread(new Runnable() { @Override public void run() { SystemClock.sleep(10000); } }).start();
The above asynchronous task and Runnable are both anonymous inner classes, so they have an implicit reference to the current Activity. If the Activity is destroyed before the task is completed, it will cause the memory resources of the Activity to be unable to be recycled, leading to memory leaks. The correct approach is still to use the static inner class method, 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) { SystemClock.sleep(10000); return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); MainActivity activity = (MainActivity) weakReference.get(); if (activity != null) { //... } } } static class MyRunnable implements Runnable{ @Override public void run() { SystemClock.sleep(10000); } } //—————— new Thread(new MyRunnable()).start(); new MyAsyncTask(this).execute();
This avoids memory resource leakage of Activity, of course, when the Activity is destroyed, the corresponding task AsyncTask::cancel() should also be canceled to avoid resource waste from background execution.
The following are common in android programming:5Memory leak problems and their corresponding solutions. If you encounter the above leak problems in programming, you might as well try the corresponding methods.
That's all for this article. I hope the content of this article can bring a certain amount of help to everyone's learning or work, and I also hope to support the呐喊 tutorial more!
Statement: The content of this article is from the Internet, the copyright belongs to the original author, the content is contributed and uploaded by Internet users spontaneously, this website does not own the copyright, has not been manually edited, and does not assume relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email to report abuse, and provide relevant evidence. Once verified, this site will immediately delete the content suspected of infringement.)