3

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?

Community
  • 1
  • 1
Kevin Pione
  • 299
  • 3
  • 12

3 Answers3

1

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.

Community
  • 1
  • 1
droidpl
  • 5,872
  • 4
  • 35
  • 47
  • Thank you for that. I have implemented worker fragments in my code to ensure tasks run independently. However I don't believe this is a correct solution to my problem. My issue is when coming back to UI thread, I can't simply wait around for the activity, so I have no way to get context at this point. – Kevin Pione Jul 08 '15 at 20:42
  • Yeah you can. Instead of pushing changes to the activity, wait the activity to get the changes by itself. I will edit my answer. – droidpl Jul 08 '15 at 20:45
0

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.

krystian71115
  • 1,927
  • 17
  • 36
  • I just tried this, and it seems to work! At least until I need to use the Activitys context to update any UI elements - my Toast for example errors now, but in a more realstic situation, I would be updating a progress spinner and changing some fragments around, all of which I will need context to do? Any Ideas? – Kevin Pione Jul 08 '15 at 20:17
0

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.

camlunt
  • 48
  • 9