Kevin Pione
Kevin Pione

Reputation: 299

Handler returning without Activity?

I am using a handler to update my UI after my thread returns - the following is the method that gets run from the thread (in a fragment)

public void signedIn(final boolean success, final String error) {
    Handler mainHandler = new Handler(getActivity().getMainLooper());
    mainHandler.post(new Runnable() {

        @Override
        public void run() {
            if(success) {
                Toast.makeText(getActivity(), "Signed In!",
                        Toast.LENGTH_SHORT).show();
            } else if (!success) {
                Toast.makeText(getActivity(), "Failed!" + error,
                        Toast.LENGTH_SHORT).show();
            }
        }
    });


}

This works fine, however if I rotate the phone at the correct time, the handler gets called BEFORE the activity has been created, so getActivity() returns null and the application crashes. I can't just wrap the method in an if (getActivity() != null), otherwise it won't update the UI at all.

What is the best way to approach this problem? Does android have anything I can use to get around this?

Thanks,

Kevin.

EDIT

So I have implemented one of the responses below that seems to work. Using a static context reference within an application class: Static way to get 'Context' on Android?

I am unsure on the details of using this so am a little hesitant - I may post another question seeing if there are issues with this?

Upvotes: 3

Views: 327

Answers (3)

camlunt
camlunt

Reputation: 48

You can use getApplicationContext() to get a suitable context for Toast. However the methods you have access to are limited by where this code is executing.

Edit:

I'm struggling to understand your methodology here. Why did you refuse to do this:

public void signedIn(boolean success, String error) {
    if(success){
        Toast.makeText(getActivity(), "Signed In!",
            Toast.LENGTH_SHORT).show();
    } 
    else{
        Toast.makeText(getActivity(), "Failed!" + error,
            Toast.LENGTH_SHORT).show();
    }
}

Making it asynchronous with the runnable created a potential race condition to access the instance of the activity. It seems like totally unnecessary complexity for a simple task of displaying a toast.

Also since booleans can only be in one of two states you don't need the else if clause.

Also change the name of this method to better align with Java naming conventions. "signedIn" should be the name of a method that returns whether or not the user is signedIn.

Upvotes: 0

droidpl
droidpl

Reputation: 5892

In this answer you can have a look on which is de best practice to handle the application configuration changes related to rotation. The best way to handle this is create a fragment with setRetainInstance(true) to keep the reference to the ongoing task.

This is needed because when you rotate the screen Android destroys your current activity (even calling the onDestroy method) and creates a new one with the new configuration. Doing so you can know when the task is finished and run the UI operation on a yet created activity.

EDIT

The fragment task should not push the results if the context is null, just store it. When the activity is reloaded, its onCreate will be called. Since you used a task fragment, you will be able to retrieve the fragment instance and know if this task has finished. The only thing the activity should do is restore its new state based on the fragment task already finished.

Upvotes: 1

krystian71115
krystian71115

Reputation: 1937

Use this to get looper:

Looper mainLooper = Looper.getMainLooper();

It works for me.

Edit: Looper.getMainLooper() returns looper of the UI Thread (the main thread).

Edit2: You can use getContext() on View to get the context.

Upvotes: 0

Related Questions