android common memory leak

1. Memory leak caused by singleton

Android's singleton pattern is very popular with developers, but if used improperly, it can also cause memory leaks. Because the static nature of the singleton makes the life cycle of the singleton as long as the life cycle of the application, 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 able to is recycled normally, which leads to a memory leak.

Here's a typical 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;

    }

}

This is a common singleton pattern. When creating this singleton, since a Context needs to be passed in, the life cycle of the Context is very important:

1. The Context of the Application is passed in: there will be no problem, because the life cycle of the singleton is as long as that of the Application;

2. The Context of the Activity is passed in: when the Activity corresponding to the Context exits, since the Context and the Activity have the same life cycle (the Activity indirectly inherits from the Context), its memory will not be used when the current Activity exits. Recycled because the singleton object holds a reference to the Activity.

 

So the correct singleton should be modified as follows:

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 eventually be used, and the singleton's life cycle is as long as the application's, thus preventing memory leaks.

 

2. Memory leaks caused by non-static inner classes creating static instances (because non-static inner classes will hold references to outer classes by default)


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 singleton of a non-static inner class is created inside the Activity, and the data of the singleton is used every time the Activity is started. Although the repeated creation of resources is avoided, this writing method will cause memory leaks, because the non-static The static inner class will hold the reference of the outer class by default, and the non-static inner class is used to create a static instance. The life cycle of the instance is as long as the application, which causes the static instance to always hold The reference of the Activity causes the memory resources of the Activity to not be recycled normally. The correct way is:

Set the inner class as a static inner class or extract the inner class and encapsulate it as 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. Usually, apis such as processing network tasks or encapsulating some request callbacks should be handled with the help of Handler. If the use of Handler is not standardized, it may cause memory leaks. 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. Since mHandler is an instance of a non-static anonymous inner class of Handler, it holds a reference to the outer class Activity. We know that the message queue is constantly polling and processing messages in a Looper thread. Then when the Activity exits, there are still unprocessed messages in the message queue or messages are being processed, and the Message in the message queue holds the reference of the mHandler instance, and the mHandler holds the reference of the Activity, so the memory resources of the Activity cannot be timely. Recycling, causing memory leaks, so another approach is:

 

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 during recycling, so although Activity leakage is avoided, there may still be pending processing in the message queue of the Looper thread , so we should remove the message in the message queue when the Activity is Destroy or Stop. The more accurate way 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);

    }

}

Use mHandler.removeCallbacksAndMessages(null); to remove all messages and all Runnables in the message queue. Of course, you can also use mHandler.removeCallbacks(); or mHandler.removeMessages(); to remove the specified Runnable and Message.

Fourth, the memory leak caused by the thread

 

Memory leaks caused by threads are also common. The following two examples may have been written by everyone:

 

//——————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();

 

Both the above asynchronous task and Runnable are an anonymous inner class, so they both 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 cannot be recovered, resulting in a memory leak. 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) {

            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();

 

In this way, the memory resource leakage of the Activity is avoided. Of course, the corresponding task AsyncTask::cancel() should be cancelled when the Activity is destroyed, so as to avoid the waste of resources when the task is executed in the background.

 

5. Memory leaks caused by unclosed resources

 

For the use of resources such as BroadcastReceiver, ContentObserver, File, Cursor, Stream, Bitmap, etc., it should be closed or logged out in time when the Activity is destroyed, otherwise these resources will not be recycled, resulting in memory leaks.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324818495&siteId=291194637