Android memory leak causes and solutions

Reprinted from: https://blog.csdn.net/u012482178/article/details/78988176
https://blog.csdn.net/u010687392/article/details/49909477

Causes of memory leaks

The memory allocated by the android system for each application is limited. When an object no longer needs to be used and should be recycled, another object in use holds its reference so that it cannot be recycled. As a result, objects that should be recycled cannot be recycled and stay in heap memory, which results in a memory leak. Memory leaks can make our app grow over time, causing app OOM errors and crashing the app.

Several cases of memory leaks

Memory leak caused by holding context

There are two kinds of context objects in Android, activity and application. The first one is often used when we pass context to a class. This causes the class to hold all references to the activity. When the Activity is closed, it cannot be recycled normally because it is held by other classes, resulting in a memory leak.

public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;//如果这个地方传入activity的context,当这个Context所对应的Activity退出时,
        //由于该Context和Activity的生命周期一样长(Activity间接继承于Context),所以当前Activity退出时它的内
        //存并不会被回收,因为单例对象持有该Activity的引用。 
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {//单例模式
            instance = new AppManager(context);
        }
        return instance;
    }
}

solution

Use the Application object when passing context to others. The life cycle of this object and the entire application coexist and die without relying on the life cycle of the activity. The reference to the context should not exceed its own life cycle. Use the static keyword carefully for the context. .

public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context.getApplicationContext();//传入application的context
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {//单例模式
            instance = new AppManager(context);
        }
        return instance;
    }
}

Memory leak caused by singleton pattern

Due to the static nature of the singleton, the life cycle of the singleton is as long as the life cycle of the application, which means that if an object no longer needs to be used, 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.
See the example above for an example.

memory leak caused by handler

For 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 mHander 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 a Looper thread that continuously polls and processes messages, then When the activity exits, there are still unprocessed or processing messages in the message queue, and the Message in the message queue holds a reference to the handler instance, and mHandler holds a reference to the Activity, so the memory resources of the activity cannot be recycled in time. , causing a memory leak.

solution

1. Put the handler in a separate class, or use a static inner class (the static inner class will not reference the activity) to avoid leaks
2. If you want to call the resources in the activity inside the handler, you can use weak The reference method points to the activity where it is located, and uses the protection of static+weakReference to disconnect the relationship between the handler and the activity.

One way to do this 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 be reclaimed during recycling. Although the activity leak is avoided, there may still be pending messages in the message queue of the Looper thread. The processed message, so we should remove the message in the message queue when the activity is destroyed or stopped, 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);
    }
}

Memory leak caused by creating static instance of non-static inner class

In order to avoid multiple initialization of resources in the project, static objects are often used to save these objects, which is also prone to memory leaks. There are several reasons:
1. The non-static inner class will hold the reference of the outer class by default
2. We use the non-static inner class to create a static instance
3. The life cycle of the static instance is as long as the application, which leads to Therefore, the static instance will always hold a reference to the activity, causing the activity to not be recycled normally.

solution

1. Change the non-static inner class to static, so that he has no reference to the outer class.
2. Extract the object and encapsulate it into a singleton.

private static TestResource mTestResource;
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    findViewById(R.id.btn).setOnClickListener(this);
}
private void initData() {
    if (mTestResource == null) {
        mTestResource = new TestResource();
    }
}
public void onClick(View v) {
    initData();
}
//非静态内部类默认会持有外部类的引用
//修改成就太之后正常被回收,是因为静态的内部类不会对Activity有引用
private static class TestResource {
}

Memory leaks caused by threads

Memory leaks caused by threads are also relatively common, as follows:


//——————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 tasks and Runnable are anonymous inner classes, so they 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.

solution

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.

Memory leak caused by unclosed resources

For the code that uses BroadcastReceiver, contentObserver File, Cursor, Stream, Bitmap and other resources, 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.

The memory leak caused by the listener not being logged out

There are many listeners that need to be registered and unregistered in Android programs. We need to ensure that the listeners are unregistered in time.

Guess you like

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