12 de abril de 2016

Android - Flow result instead of Activity result or how to get a result between activities

    • How many times have you needed to return a result from one Activity to another?
    • A lot of times, actually, I don't remember any app that I've writen without that functionality.
    • was that difficult? How did you do it?
    • It wasn't, in fact it was pretty easy. Android has a method startActivityForResult(Intent, int) that allows us to launch an Activity waiting for its result in a method called onActivityResult(int, int, Intent), no mistery there.
    • Well, and what happens when there is more than one Activityinvolved in the process?
    • ah, it is very easy too, when the second Activity of the flow is launched you have to add a the flag Intent.FLAG_ACTIVITY_FORWARD_RESULT to the Intent and then it will be your next Activity who will be in charge to call setResult to deliver the result.
    • is that all?
    • Well, it is not, you need to call Activity.finish in the first Activity of the flow, else when the user ends in the second one, he will get back to the first one and he will need to click back to leave the flow and receive the result.
    • That seems a little bit... odd?
    • But you can do Activity.finish as I said before, in that way when the user finishes the second Activity the flow will be finished as well.
    • and, what happens if the user clicks back in the last Activity of the flow?
    • That it leaves the flow. 
    • But then the user loses all the data provided during the flow, is that right?
    • Yes.
    • And what if instead of a flow with 2 activities we have a flow with 5, 6 or even 10 activities?
    • Then the user would need to start the flow from the beginning.
    • Well, that was the point from the beginning, I've also faced that problem and so far, the solution presented below is the one that I like more.

    How?

    This is very easy and in only a few steps.
    • First we need to start the first Activity of the flow like startActivityForResult:
    
        Intent intent = new Intent(this, ProfileNameStepActivity.class);
        startActivityForResult(intent, PROFILE_REQUEST_CODE_FLOW);
        
    
    • Then we wait the result like we always do when there is only one Activity in the flow.
    
      @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == PROFILE_REQUEST_CODE_FLOW && resultCode == RESULT_OK) {
          Profile profile = ExtrasUtils.getProfile(data);
          onProfileCompleted(profile);
        } else {
          super.onActivityResult(requestCode, resultCode, data);
        }
      }
      
    
    • Also in the last Activity of the flow we add (like always): 
    
        Intent intent = new Intent();
        intent.putExtra(ExtrasUtils.EXTRA_PROFILE,
            new Profile.Builder().withName(name).withAddress(address).build());
        setResult(RESULT_OK, intent);
        finish();
    
    
    So far we haven't done anything special thing, this code we've written hundreds of times.
    This is the interesting part...
    • Now we start any other Activity involved in the flow with startActivityForResult, while this time the requestCode is not important because we don't wait for it result:
    
          /*   
          We can pass any number less the ones that we
          listen here, in this case ADDRESS_REQUEST_CODE_FLOW 
          (unless we want to start a sub-flow, I'll explain that later
          */
          Intent intent = new Intent(this, ProfileFinalStepActivity.class);
          startActivityForResult(intent, 0);
    
    
    • Finally we need this piece of code in our BaseActivity and all our activities will inherit from it.
    
      @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        
        if (resultCode == RESULT_OK) {
          setResult(resultCode, data);
          finish();
        }
      }
    
    
    This last part of the code is the one that does the magic, if you pay attention, we have started all our activities in the flow with startActivityForResult, so when the next Activity is finished our activities will receive a call in onActivityResult, and we do check if we already got a result there from the next Activity, so if the Activity receives the result, we set the result for the current Activity and the previous one will do the same until any Activity has its onActivityResult overriden and it is waiting for the requestCode.
    This same technique can be used to include sub-flows inside a flow, imagine for example that the user can do shopping in your application and before the user pays it has to provide some basic info like the name or the address (or a complete profile).
    The same code that we have used to start/finish a flow, we can use it in any step of the flow to add a sub-flow and it will work just in the same way.

    What have we achieved with this?

    • Now we are able to start/finish a flow in the same place.
    • We can deliver a result of a flow (group of activities) very easy.
    • We can add additional flows inside a flow easily.

2 comentarios:

  1. Thanks! This is the perfect explanation that I was looking for. Everyone was talking about the forward flag but then... what happens when you click back! So I implemented the activityforresult without knowing if it was the good way to go. Thanks!

    ResponderEliminar
  2. The Casinos & Casinos Near You (Mapyro)
    Find the closest casinos and places 청주 출장안마 to go near you 경상남도 출장샵 (mapyro) in New Jersey. 광양 출장안마 광주광역 출장샵 New York City: 6.5/10 · 용인 출장마사지 New York City: 6.5/10.

    ResponderEliminar