hunterp
hunterp

Reputation: 15986

Why does my Handler reference a null Activity?

Inside of the handler after I first enter the app from a clean state, the Handler handles the MSG_PULLED action however, the reference to main is null. The weak reference is not null. How can this possibly be happening?

Inspired by this post: This Handler class should be static or leaks might occur: IncomingHandler

static class MainHandler extends Handler {
    private final WeakReference<MainActivity> wMain; 
    static int angle=0;
    public MainHandler(MainActivity main) {
        super();
        this.wMain = new WeakReference<MainActivity>(main);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity main = wMain.get();
        int what = msg.what;
        if(what == MSG_PULLED) {
            main.startAnim();
        } 
    }
}

And how I initiate the handler:

static MainHandler mainHandler;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mainHandler = new MainHandler(this);
        mainHandler.sendEmptyMessageDelayed(MSG_PULLED,500);
    }

Upvotes: 0

Views: 1428

Answers (1)

zapl
zapl

Reputation: 63955

The Activity instances in your app are regularly destroyed and new ones are created for example when rotating the display.

Now what should happen in that case is that the old instance is garbage collected and only the new one exists. If you keep the old one around you have created a leak.

Since Handler are not garbage collectable until they have no more messages (?) they can live longer than the Activity in witch they were created, which usually leads to leaking the old Activity (until the Handler can be collected) since Handler usually have a strong reference to their Activity.

The WeakReference way in your code get's rid of that problem by keeping just a weak link to the Activity that does not prevent garbage collection.

The problem is that you use the get() method the wrong way: get() will only return the original object while it exists. When it's gone you get null. Here: the Activity will exists while it is still the active one (determined by the system).

The null is also not a big problem: when you get null your Activity instance is no longer alive (maybe a new one was created, maybe it's completely gone) so you can't do anything useful with it anymore. Animation would not show even if you had still a reference.

Basically do it like below and your problem is solved

@Override
public void handleMessage(Message msg) {
    super.handleMessage(msg);
    MainActivity main = wMain.get();

    // message arrived after activity death
    if (main == null) return;

    int what = msg.what;
    if(what == MSG_PULLED) {
        main.startAnim();
    } 
}

The WeakReference itself (wMain) is not null because it is itself strongly references as a member variable. Just the content inside it can / will be null at some point.

Upvotes: 1

Related Questions