1

I am re-submitting this question, as I don't think my last one really went into the problem I was having.

So I have a thread that returns and then uses a handler to update the UI as the following:

    public void completeSignIn(final boolean success, final String error) {
    Log.d(Constants.LogTag, "Finalising Sign In...");
    final Looper mainLooper = Looper.getMainLooper();
    final Handler mainHandler = new Handler(mainLooper);
    mainHandler.post(new Runnable() {

        @Override
        public void run() {
            if (success) {
                TextView tv = (TextView) getActivity().findViewById(R.id.register);
                tv.setText("SIGNED IN!!");
            } else if (!success) {
            }
        }
    });
}

The problem I am having is that because the thread could return at any point after it is done querying the server, there is not gaurantee that getActivity() will return the Activity. I have found that if I rotate my device as the thread is about to return, this bit of code can be called inbetween the activities destroy/create cycle. I am unsure if it is important that I am using a fragment here, but I don't think there is any harm in using the parent activity to update views?

So I am unsure how I could force the handler to wait until the activity is created - is this possible, or is thee a standard way of dealing with this? As I don't see this in other apps I have tested.

UPDATE

I put some logs in my fragment and managed to get the following to illustrate my issue:

07-09 22:17:15.164 25435-25435/? D/Kevins_Tag﹕ Detaching Activity...

07-09 22:17:15.234 25435-26702/? D/Kevins_Tag﹕ Signing in...

07-09 22:17:15.234 25435-26702/? E/Kevins_Tag﹕ Activity is null

07-09 22:17:15.234 25435-26687/? D/Kevins_Tag﹕ Finalising Sign In...

07-09 22:17:15.284 25435-25435/? D/Kevins_Tag﹕ Attaching Activity...

07-09 22:17:15.284 25435-25435/? D/Kevins_Tag﹕ Activity Exists

As you can see, the thread is calling the UI in between detach and attach...

Kevin Pione
  • 299
  • 3
  • 12
  • `I have found that if I rotate my device as the thread is about to`. The `Activity` is recreated. Please see http://stackoverflow.com/questions/2620917/how-to-handle-an-asynctask-during-screen-rotation and http://stackoverflow.com/questions/7128670/best-practice-asynctask-during-orientation-change – Jared Burrows Jul 09 '15 at 19:11
  • My issue is not with the thread being restarted when the activity re-creates. I am aware how I should handle this (worker fragments). My issue is that when the thread returns to the UI, it can be inbetween deletion and creation. I also don't think just turning off the config changes is a good solution. I know there must be a correct way of handling this - but am really struggling to find it, – Kevin Pione Jul 09 '15 at 19:17
  • `My issue is not with the thread being restarted when the activity re-creates` Read the links. – Jared Burrows Jul 09 '15 at 20:00
  • I have read the links, they detail how to retain a task after orientation change, which is fine but not my problem. – Kevin Pione Jul 09 '15 at 20:02

2 Answers2

1

I don't know for sure if this could solve your problem, but one thing I would try is check for an existing activity before do anything, like this:

//any code

Activity activity = getActivity();

if(activity != null) {
    if (success) {
        TextView tv = (TextView) activity.findViewById(R.id.register);
        tv.setText("SIGNED IN!!");
    } else if (!success) {
    }
}

//any code
Álisson Morais
  • 356
  • 2
  • 19
  • Thanks - I tried that just to capture the error in the first place. Unfortunatelt just ignoring the task finishing won't help - as a well timed rotation could essentially cancel a sign-in request then – Kevin Pione Jul 09 '15 at 19:24
  • Maybe if try this, could help you **``**. But a think that a better performance could be achieved if you use _Service_ instead of an _Activity_ to do this. – Álisson Morais Jul 09 '15 at 19:35
  • Thank you, but this will just be masking the problem - I want to deal with it correctly – Kevin Pione Jul 09 '15 at 19:48
  • @ÁlissonMorais comment does work but I hear it is not the best way. – Jared Burrows Jul 09 '15 at 20:00
  • No, it is not the best way. In an _Activity_ i think the best way is using the methods _onSaveInstanceState()_ and _onRestoreInstanceState()_ . Accord to your project, maybe you will need to create a class that implements the _Parcelable_ interface to use as parameter. You can read more about it in: [Android Developer Reference](https://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle)). This answer maybe be useful to: [onSaveInstanceState () and onRestoreInstanceState () - Stack Overflow](http://stackoverflow.com/a/4101501/4973904) – Álisson Morais Jul 09 '15 at 22:19
1

As you explain in your question, method completeSignIn() is in one of your fragments. The Runnable that completeSignIn() contains is an inner class of the fragment, and as such, holds a reference to the fragment. That is how the code in the Runnable can call fragment method getActivity()--the call is made using the "hidden" fragment reference.

You correctly observe that during a configuration change, completeSignIn() can get called after the fragment has been detached from the activity, resulting in a null activity reference. You ask:

I am unsure how I could force the handler to wait until the activity is created

That is not possible in this case. However, even if it were, it wouldn't help. The fragment that completeSignIn() has a reference to is dead. It was detached and destroyed and will eventually be garbage collected. When your activity is recreated, the old fragment is not used--a new fragment is constructed and goes through the creation lifecyle steps using the saved state of the old fragment.

It's difficult to suggest an alternative approach without knowing how the overall server sign-in status and display is managed. One option would be to maintain the status in a singleton in addition to updating the sign-in view when sign-in completed. For configuration changes, where the view may not have been updated because it was inaccessible, the activity would be responsible for updating the view when the activity is recreated, using the status from the singleton.

Bob Snyder
  • 37,759
  • 6
  • 111
  • 158