How can Android effectively solve the problem of Memory leakage and android leakage?
Recently, I have been studying Handler's knowledge about how to avoid memory overflow caused by Handler. I found a lot of information on the Internet, many of which are copied from each other and have no practical effect.
The memory leak detection tool in this article is: LeakCanary github address: https://github.com/square/leakcanary
What is memory leakage?
- Memory leakage occurs when the program no longer uses the memory and the memory is released fails, resulting in useless memory consumption. Memory leakage does not mean that the physical memory disappears. The memory leakage here is the memory allocated by the program, but the program loses control over the memory due to program logic errors, memory waste.
How can this cause memory leakage?
- Memory leakage caused by resource objects not closed. For example, the cursor is not closed after the database is queried.
- ConvertView is not used for reuse when creating an Adapter
- Call recycle () to release memory when the Bitmap object is not in use
- Objects are referenced by objects with a long life cycle. If an activity is referenced by a static set, the activity cannot be released.
What are the dangers of Memory leakage?
Memory leakage does not directly harm the app. Even if the app has a memory leak, it may not necessarily cause the app to crash, but it will increase the app memory usage. Memory cannot be released, which will slowly cause app memory overflow. Therefore, the purpose of solving Memory leakage is to prevent memory overflow in the app.
1. Activity Memory leakage caused by new threads
Example:
Package rxnet.zyj.com. myapplication; import android. support. v7.app. appCompatActivity; import android. OS. bundle; import android. view. view; public class Activity6 extends AppCompatActivity {@ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_6); findViewById (R. id. finish ). setOnClickListener (new View. onClickListener () {@ Override public void onClick (View v) {finish () ;}}); new Thread (new Runnable () {@ Override public void run () {try {
// Simulate time-consuming operations Thread. sleep (15000);} catch (InterruptedException e) {e. printStackTrace () ;}}). start ();}}
After running the above Code, click the finish button. a memory leak occurs later.
Why does Activity6 cause memory leakage?
Go to the Activity6 interface and click finish to destroy Activity6, but the thread in Activity6 is still running. The anonymous internal class Runnable object references the Activity6 instance, as a result, the memory occupied by Activity6 cannot be recycled by GC in a timely manner.
How can we improve it?
Change Runnable to static non-Anonymous internal class.
package rxnet.zyj.com.myapplication;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;public class Activity6 extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_6); findViewById( R.id.finish).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); new Thread( new MyRunnable()).start(); } private static class MyRunnable implements Runnable { @Override public void run() { try { Thread.sleep( 15000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } }
2. Activity Memory leakage caused by adding listeners to Activity
package rxnet.zyj.com.myapplication;import android.app.Activity;import android.os.Bundle;public class LeakActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); NastyManager.getInstance().addListener(this); }}
This is a common mistake in development, NastyManager. getInstance () is a singleton. When we use addListener (this) to bind the Activity as a Listener to the NastyManager, a bad thing will happen.
How can we improve it?
To fix such a Bug, it is actually quite simple, that is, when your Acitivity is destroyed, you just need to unbind it from NastyManager.
package rxnet.zyj.com.myapplication;import android.app.Activity;import android.os.Bundle;public class LeakActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); NastyManager.getInstance().addListener(this); } @Override protected void onDestroy() { super.onDestroy(); NastyManager.getInstance().removeListener(this); }}
3. Does Handler anonymous internal class cause memory overflow?
First look at a piece of code
package rxnet.zyj.com.myapplication;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;public class HandlerActivity extends AppCompatActivity { private final static int MESSAGECODE = 1 ; private final Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.d("mmmmmmmm" , "handler " + msg.what ) ; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); new Thread(new Runnable() { @Override public void run() { handler.sendEmptyMessage( MESSAGECODE ) ; try { Thread.sleep( 8000 ); } catch (InterruptedException e) { e.printStackTrace(); } handler.sendEmptyMessage( MESSAGECODE ) ; } }).start() ; }}
After the code is run, click the finish button to detect the memory leakage in HandlerActivity. After the Activity is finished, the delayed message will continue to exist in the main thread message queue for 8 seconds, and then process the message. The message references the Handler object of the Activity, and then the Handler references the Activity. These referenced objects are retained until the message is processed. As a result, the Activity object cannot be recycled, leading to Activity leakage. Handler is a very common and useful class, asynchronous, thread security, and so on. What will happen if the following code is available? Handler. postDeslayed. Assume that the delay time is several hours... What does this mean? It means that as long as the handler message has not been processed, it will remain alive, and the Activity containing it will be alive. We will try to fix it. The solution is WeakReference, which is called weak reference. The garbage collector ignores weak references during garbage collection, so the Activity containing the Garbage Collector will be cleared normally.
How to avoid
- Use static internal classes
- Use Weak references
The modified code is like this.
package rxnet.zyj.com.myapplication;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import java.lang.ref.WeakReference;public class HandlerActivity extends AppCompatActivity { private final static int MESSAGECODE = 1 ; private static Handler handler ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); handler = new MyHandler( this ) ; new Thread(new Runnable() { @Override public void run() { handler.sendEmptyMessage( MESSAGECODE ) ; try { Thread.sleep( 8000 ); } catch (InterruptedException e) { e.printStackTrace(); } handler.sendEmptyMessage( MESSAGECODE ) ; } }).start() ; } private static class MyHandler extends Handler { WeakReference<HandlerActivity> weakReference ; public MyHandler(HandlerActivity activity ){ weakReference = new WeakReference<HandlerActivity>( activity) ; } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if ( weakReference.get() != null ){ // update android ui Log.d("mmmmmmmm" , "handler " + msg.what ) ; } } }}
This Handler has used static internal classes and uses weak references. However, this does not completely solve the HandlerActivity Memory leakage problem. The culprit is that the thread creation method has a problem, just like the first example in this article. The improved method is to write the Runnable class as a static internal class.
The complete code is as follows:
Package rxnet.zyj.com. myapplication; import android. OS. bundle; import android. OS. handler; import android. OS. message; import android. support. v7.app. appCompatActivity; import android. util. log; import android. view. view; import java. lang. ref. weakReference; public class HandlerActivity extends AppCompatActivity {private final static int MESSAGECODE = 1; private static Handler handler; @ Override protected voi D onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_handler); findViewById (R. id. finish ). setOnClickListener (new View. onClickListener () {@ Override public void onClick (View v) {finish () ;}}); // create Handler handler = new MyHandler (this ); // create a Thread and start the Thread new Thread (new MyRunnable ()). start ();} private static class MyHandler extends Handler {WeakReference <HandlerActivity> weakReference; public MyHandler (HandlerActivity activity) {weakReference = new WeakReference <HandlerActivity> (activity) ;}@ Override public void handleMessage (Message msg) {super. handleMessage (msg); if (weakReference. get ()! = Null) {// update android ui Log. d ("mmmmmmmm", "handler" + msg. what) ;}} private static class MyRunnable implements Runnable {@ Override public void run () {handler. sendEmptyMessage (MESSAGECODE); try {Thread. sleep (8000);} catch (InterruptedException e) {e. printStackTrace ();} handler. sendEmptyMessage (MESSAGECODE );}}}
Wait. It's not over yet?
The code above has effectively solved the Handler. Runnable references the Activity instance and causes memory leakage, but this is not enough. The core cause of Memory leakage is that this object is referenced by other objects when the system recycles the memory, causing the memory to fail to be recycled. So we always need to stretch this string when writing code. Return to the above question. When the current Activity calls finish to destroy, should all threads in the Activity be canceled in the OnDestory () method. Of course, whether to cancel the asynchronous task depends on the specific requirements of the project. For example, when the Activity is destroyed, a thread is started to asynchronously write log logs to the local disk, but the OnDestory () method. Therefore, making a selection based on the current environment is the correct solution.
So we can also modify the code to: Remove all callback and messages from onDestroy.
Package rxnet.zyj.com. myapplication; import android. OS. bundle; import android. OS. handler; import android. OS. message; import android. support. v7.app. appCompatActivity; import android. util. log; import android. view. view; import java. lang. ref. weakReference; public class HandlerActivity extends AppCompatActivity {private final static int MESSAGECODE = 1; private static Handler handler; @ Override protected voi D onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_handler); findViewById (R. id. finish ). setOnClickListener (new View. onClickListener () {@ Override public void onClick (View v) {finish () ;}}); // create Handler handler = new MyHandler (this ); // create a Thread and start the Thread new Thread (new MyRunnable ()). start ();} private static class MyHandler extends Handler {WeakReference <HandlerActivity> weakReference; public MyHandler (HandlerActivity activity) {weakReference = new WeakReference <HandlerActivity> (activity) ;}@ Override public void handleMessage (Message msg) {super. handleMessage (msg); if (weakReference. get ()! = Null) {// update android ui Log. d ("mmmmmmmm", "handler" + msg. what) ;}} private static class MyRunnable implements Runnable {@ Override public void run () {handler. sendEmptyMessage (MESSAGECODE); try {Thread. sleep (8000);} catch (InterruptedException e) {e. printStackTrace ();} handler. sendEmptyMessage (MESSAGECODE) ;}@ Override protected void onDestroy () {super. onDestroy (); // such If the parameter is null, all Callbacks and Messages are cleared. Handler. removeCallbacksAndMessages (null );}}
4. AsyncTask causes memory leakage
package rxnet.zyj.com.myapplication;import android.os.AsyncTask;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;public class Activity2 extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_2); findViewById( R.id.finish2).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); new AsyncTask<String,Integer,String>(){ @Override protected String doInBackground(String... params) { try { Thread.sleep( 6000 ); } catch (InterruptedException e) { } return "ssss"; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); Log.d( "mmmmmm activity2 " , "" + s ) ; } }.executeOnExecutor( AsyncTask.THREAD_POOL_EXECUTOR , "" ) ; }}
Why?
The code above creates an anonymous AsyncTask class in the activity. The Anonymous class is the same as the non-static internal class and will hold the external Class Object. Here, this is the activity, therefore, if you declare and instantiate an anonymous AsyncTask object in the Activity, memory leakage may occur. If this thread is still executed in the background after the Activity is destroyed, the thread will continue to hold the reference of this Activity and will not be recycled by GC until the thread execution is complete.
How can this problem be solved?
- Custom static AsyncTask class
- The AsyncTask cycle is consistent with the Activity cycle. That is, AsyncTask cancel should be dropped at the end of the Activity lifecycle.
Package rxnet.zyj.com. myapplication; import android. OS. asyncTask; import android. support. v7.app. appCompatActivity; import android. OS. bundle; import android. view. view; public class AsyncTaskActivity extends AppCompatActivity {private static MyTask myTask; @ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_asynctask); findV IewById (R. id. finish ). setOnClickListener (new View. onClickListener () {@ Override public void onClick (View v) {finish () ;}}); myTask = new MyTask (); myTask.exe cuteOnExecutor (AsyncTask. THREAD_POOL_EXECUTOR, "");} private static class MyTask extends AsyncTask {@ Override protected Object doInBackground (Object [] params) {try {// simulate time-consuming Thread operations. sleep (15000);} catch (InterruptedException e) {e. PrintStackTrace ();} return "" ;}}@ Override protected void onDestroy () {super. onDestroy (); // cancel an asynchronous task if (myTask! = Null) {myTask. cancel (true );}}}
5,
Memory leakage caused by Timer Tasks
Package rxnet.zyj.com. myapplication; import android. OS. bundle; import android. support. v7.app. appCompatActivity; import android. view. view; import java. util. timer; import java. util. timerTask; public class TimerActivity extends AppCompatActivity {@ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_2); findViewById (R. id. finish2 ). setOnClickListener (new View. onClickListener () {@ Override public void onClick (View v) {finish () ;}}); // start scheduled task timer ();} void timer () {new Timer (). schedule (new TimerTask () {@ Override public void run () {while (true) ;}, 1000); // start a task in 1 second }}
Why?
Here, the memory leakage is that Timer and TimerTask do not perform Cancel, so that Timer and TimerTask always reference the external class Activity.
How can this problem be solved?
- Perform Cancel at an appropriate time.
- TimerTask uses static internal classes
Note: Some information on the internet says that TimerTask Memory leakage can be used.Perform Cancel at an appropriate time. Tested and provedMemory leakage still occurs when you use Cancel at the right time. Therefore, you must use static internal classes.
Package rxnet.zyj.com. myapplication; import android. OS. bundle; import android. support. v7.app. appCompatActivity; import android. util. log; import android. view. view; import java. util. timer; import java. util. timerTask; public class TimerActivity extends AppCompatActivity {private TimerTask timerTask; @ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setConten TView (R. layout. activity_2); findViewById (R. id. finish2 ). setOnClickListener (new View. onClickListener () {@ Override public void onClick (View v) {finish () ;}}); // start scheduled task timer ();} void timer () {timerTask = new MyTimerTask (); new Timer (). schedule (timerTask, 1000); // start a task after 1 second} private static class MyTimerTask extends TimerTask {@ Override public void run () {while (true) {Log. d ("ttttttttt", "t ImerTask ") ;}}@ Override protected void onDestroy () {super. onDestroy (); // cancel the scheduled task if (timerTask! = Null) {timerTask. cancel ();}}}