Thursday, November 10, 2016

Android - Handlers and Data Leaks

This post is mostly for my reference as I use Handlers a lot, but I'm still trying to get use to the idea of a WeakReference. Obviously this isn't rocket science, but up until about 9 months ago I used Handlers in a very improper way (my first example) and undoubtedly ended up with a lot of data leaks in any multi-threaded application I developed.

I am writing this because I recently I was being tested on my Android knowledge, and although I knew about data leaks and how to solve the data leaks, courtesy of Android Studio, I didn't know what was actually causing the data leaks.

If not properly used a Handler will hold a reference to the context of the activity from which it was created which therefore prevents the garbage collector from performing garbage collection on the Activity from which you instantiated the handler. Here's a good example of what NOT to do.
class FooActivity extends Activity {
    private Handler mHandler;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_foo);
        mHandler = createMyHandler();
        createBackgroundThreadWithHandler(mHandler);
    }
    private Handler createMyHandler() {
        return new Handler() {
            @Override handleMessage(Message msg) {
               super.handleMessage(msg);
               Toast.makeText(FooActivity.this, "Hi", Toast.LENGTH_SHORT).show();
            }
        };
    }
}

The above code is not good because we have no idea how long the BackgroundThreadWithHandler will be alive, however as long as it is, it will have a reference to the handler which will have a reference to the context of FooActivity.

So now lets look at the proper way to handle dealing with Handlers. We need to be able to create the handler, however we can't let the handler hold a strong reference to the Context/Activity.

class FooActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_foo);
        createBackgroundThreadWithHandler(new MyHandler(FooActivity.this));
    }    

    void sayHi() {
        Toast.makeText(FooActivity.this, "Hi", Toast.LENGTH_SHORT).show();
    }

    static class MyHandler extends Handler {
        MyHandler(FooActivity fooActivity) {
            weakFooActivity = new WeakReference(fooActivity);
        }   
        @Override handleMessage(Message msg) {
            super.handleMessage(msg);
            FooActivity fooActivity = weakFooActivity.get();
            if (fooActivity != null) {
                fooActivity.sayHi();
            }
        }
    }
}

This example keeps a weak reference to FooActivity which allows the garbage collector to still perform garbage collection on FooActivity.

No comments:

Post a Comment