2

I'm writing this StackOverflow question because I haven't read a clear question resolution about the Android onActivityResult functioning when fragments AND activities are involved. What I would like to have (even though I'm pretty sure I won't have much participation) would be to have elements here to understand how onActivityResult in each case, as some of them seem quite random.

From what I've seen on the questions on SO, there is the classic case (startActivityForResult and onActivityResult on the same activity) and 3 confusing/not working cases:

  • Calling startActivityForResult from a fragment and using onActivityResult on the parent activity

  • Calling startActivityForResult from an activity and using onActivityResult on a fragment belonging to the activity

  • Calling startActivityForResult from a fragment and using onActivityResult on the fragment

My first question is: is the Android API ready for those use cases? And then the main question is: how does it work? Because even with the Google reference, the only use case I have seen being described was the classic case. The other cases seem unclear to me, from what I have read on other questions about it.

A.Danibo
  • 494
  • 1
  • 4
  • 16

2 Answers2

2

I hope I can help you. Despite the lack of doc, you can browse the source code and check how they works. What I could observe and I might be wrong is:

Starting from Activity

Activity.startActivityForResult(requestCode) --- > Activity.onActivityResult(requestCode)

Starting from Fragment

Fragment.startActivityForResult(requestCode) -> Activity.startActivityForResult(maskedRequestCode) -> |
                                                                                                      |
                                                                                                      |
  Fragment.startActivityForResult(requestCode) <--  Activity.onActivityResult(maskedRequestCode)  < - |

As you can see, when you invoke Fragment.startActivityForResult, the fragment will invoke the similar method from host Activity. However, the host Activity will mask that requestCode in order to know which fragment triggered that action. This way, during the onActivityResult, the Activity will unmask that value in order to deliver the result to proper fragment (since the activity may be hosting several fragments etc).

So, based on that, you should start an activity and handle its result on the same element. Otherwise, you won't be able to determine the requestCode properly. Because if a Fragment started the activity, hosting activity will have access to the masked value only (without coppyting the code to unmask the value, you can't determine the requestCode to take proper actions).

Remember that you can communicate with your activity from a fragment:

class MainActivity {
    public void startActivityX() {
        startActivityForResult(ACTIVITY_X_REQUEST_CODE);
    }
}

class MainFragment {
    public void startActivityX() {
        ((MainActivity)getActivity()).startActivityX();
    }
}

Inverse is also valid.. You can get the instance of the Fragment, cast to MainFragment and invoke the methods that you need.

Basically, you should:

  • If the start/result is handled by the Activity, start and handle via Activity

  • If the start/restult will be handled by the Fragment, start start and handle via Fragment

  • If the start/result is shared between Fragment and Activity, start and handle it via Activity. Then, the activity calls back the methods on the Fragment. In your Fragment, you can get the Activity via getActivity(). Also, on the Activity, you can get the reference of the Fragment via getFragmentByTag() So, it is possible to invoke methods of a element from the other element and vice versa.

HERE I also shared how the requestCode is masked etc. It may help you.

guipivoto
  • 18,327
  • 9
  • 60
  • 75
1

W0rmH0le's answer is good, and I agree with what he wrote. However, I wanted to shed some light on why people think that Fragment.startActivityForResult() is broken.

onActivityResult() does not throw an exception if you fail to call super.onActivityResult(). However, you must call super if you override this method in your Activity and want to be able to handle results in your Fragments.

super.onActivityResult() is the mechanism that unmasks the requestCode and dispatches the result to the Fragment. So, if your Activity has code that looks like this:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == foo) {
        // do something
    }
}

This will break your fragments. Add a super call:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == foo) {
        // do something
    }
}

Now the masked requestCode will be handled correctly, and the result will be dispatched to your Fragment as expected.

Ben P.
  • 52,661
  • 6
  • 95
  • 123
  • Awesome information that I never saw on any other thread. Can't validate both answers but this is also a great tip. Thanks a lot! – A.Danibo Jun 06 '19 at 09:03