Handler programming issues

The Handler class is truly fundamental to the Android platform and is used widely throughout, but there are plenty of ways we can get ourselves into trouble if we aren't careful.

Leaking implicit references

The biggest worry when using Handler within an Activity is resource leakage which, just as with AsyncTask, is very easy to do. Here's one of our earlier examples:

final Runnable runnable = new Runnable(){
    public void run() {
        // … do some work
    }
};
handler.postDelayed(runnable, TimeUnit.SECONDS.toMillis(10));

By declaring an anonymous inner Runnable inside an activity, we have made an implicit reference to that containing Activity instance. We've then posted the Runnable to a handler and told it to execute in 10 seconds time.

If the Activity finishes before the 10 seconds are up, it cannot yet be garbage collected because the implicit reference in our Runnable means that the Activity is still reachable by live objects.

So, although it makes for a concise example, it is not a good idea in practice to post non-static Runnables onto the main thread's Handler queue (especially with postDelayed or postAtTime) unless we're very careful to clean up after ourselves.

One way to minimize this problem is to avoid using non-static inner classes; for example, by always declaring Runnables as top-level classes in their own file, or as static classes in an Activity subclass. This means that references must be explicit, which makes them easier to spot and nullify.

In addition, we can cancel pending tasks during Activity lifecycle callbacks such as onPause. This is easiest if we're working with Messages since we can remove them by their what value, and don't have to keep references as we would with Runnables.

For HandlerThread instances we've created, we should make sure to quit when the Activity is finishing, which will prevent further execution and free up the Runnable and Message objects for garbage collection.

Leaking explicit references

If we are to interact with the user interface, we'll at least need a reference to an object in the View hierarchy, which we might pass into our static or top-level Runnable's constructor.

static class MyRunnable implements Runnable {
    private View view;
    public MyRunnable(View view) {
        this.view = view;
    }
    public void run() {
        // … do something with the view.    
    }
}

However, by keeping a strong reference to the View, we are again subject to potential memory leaks if our Runnable outlives the View; for example, if some other part of our code removes this View from the display before our Runnable executes.

One solution to this is to use a weak reference, and check for null before using the referenced View.

static class MyRunnable implements Runnable {
    private WeakReference<View> view;
    public MyRunnable(View view) {
        this.view = new WeakReference<View>(view);
    }
    public void run() {
        View v = view.get(); // might return null
        if (v != null) {
            // … do something with the view.
        }
    }
}

If you haven't used WeakReference before, what it gives us is a way to refer to an object only for as long as some other live object has a stronger reference to it (for example, a "normal" property reference).

When all strong references are garbage collected, our WeakReference will also lose its reference to the View, get() will return null, and the View will be garbage collected.

This fixes the resource leakage problem, but we must always check for null before using the returned object, to avoid potential NullPointerException's.

If we're sending Messages to our Handler and expecting it to update the user interface, it will also need a reference to the View hierarchy. A nice way to manage this is to attach and detach the Handler from onResume and onPause.

private static class MyHandler extends Handler {
    private TextView view;
    public void attach(TextView view) {
        this.view = view;
    }
    public void detach() {
        view = null;
    }
    @Override
    public void handleMessage(Message msg) { 
        //…
    }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.61.147