Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can this code avoid the Android handler memory leak?

handler1 is a leak.

I want to convert handler1 code to handler2 code. Is that OK?

What is the difference between the two codes?

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // leaks!
        Handler handler1 = new Handler()
        {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.e("LOG", "Hello~1");
            }
        };

        Handler handler2 = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                Log.e("LOG", "Hello~2");
                return false;
            }
        });

        handler1.postDelayed(new Runnable() {
            @Override
            public void run() { }
        }, 60000);
        handler2.postDelayed(new Runnable() {
            @Override
            public void run() { }
        }, 60000);

        finish();
    }
}
like image 929
user2324746 Avatar asked Jan 01 '23 21:01

user2324746


1 Answers

Why Leak Warning for handler1?

For the reason on leak warning, this article explains very well.

Quoting from the article

In Java, non-static inner and anonymous classes hold an implicit reference to their outer class. Static inner classes, on the other hand, do not.

So when you created handler1 by anonymous class, it will holds a reference to the MainActivity instance and MainActiviy can not be garbage collected.

Solution

Quoting from the article again

To fix the problem, subclass the Handler in a new file or use a static inner class instead. Static inner classes do not hold an implicit reference to their outer class, so the activity will not be leaked. If you need to invoke the outer activity’s methods from within the Handler, have the Handler hold a WeakReference to the activity so you don’t accidentally leak a context. To fix the memory leak that occurs when we instantiate the anonymous Runnable class, we make the variable a static field of the class (since static instances of anonymous classes do not hold an implicit reference to their outer class):

Following the article, update your code as follows:

public class MainActivity extends AppCompatActivity {

    private static class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
             Log.e("LOG", "Hello~1");
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Handler handler1 = new MyHandler();

        handler1.postDelayed(new Runnable() {
            @Override
            public void run() { }
        }, 60000);

        finish();
    }
}

Can handler2 solve the problem?

The answer from @Michael This Handler class should be static or leaks might occur: IncomingHandler provides the solution.

Quoting from @Michael answer

As I understand it, this will not avoid the potential memory leak. Message objects hold a reference to the mIncomingHandler object which holds a reference the Handler.Callback object which holds a reference to the Service object. As long as there are messages in the Looper message queue, the Service will not be GC. However, it won't be a serious issue unless you have long delay messages in the message queue.

In your case, handler2 will hold a reference to Handler.Callback object.And since Handler.Callback is created by anonymous class, hence it will hold a reference to MainActiviy instance too. So MainActiviy instance can not be garbage collected also.

like image 141
samabcde Avatar answered Jan 15 '23 19:01

samabcde