9 de abril de 2016

Android - Flow result en lugar de Activity result o como obtener un resultado entre actividades

    • ¿Cuantas veces te has encontrado que necesitas entregar un resultado de una Activity a otra?
    • Muchas, no recuerdo ninguna aplicación donde no lo haya tenido que hacer.
    • ¿Te resultó difícil? ¿Como lo hiciste?
    • No, de hecho fue muy fácil, Android siempre ha tenido un método startActivityForResult(Intent, int) para lanzar una Activity de la que esperas un resultado y otro método onActivityResult(int, int, Intent) para recibirlo, no tiene ningún misterio.
    • Bien ¿y que pasa si hay más de una Activity implicada en el proceso?
    • Ah, también es muy fácil, cuando lanzas la segunda Activityimplicada en el flow lo haces añadiendo el flag Intent.FLAG_ACTIVITY_FORWARD_RESULT al Intent y entonces será tu siguiente Activity la encargada de llamar a setResult para entregar el resultado.
    • ¿Eso es todo?
    • Bueno, en realidad no, en realidad tienes que hacer un Activity.finish de la primera Activity del flow porque sino cuando el usuario termine la segunda, volverá a la primera y tendrá que salir manualmente de esta para recibir el resultado.
    • Mmmm... ¿eso no es muy elegante no?
    • Bueno, puedes llamar a Activity.finish como he dicho antes, de esta forma cuando el usuario termina en la segunda Activity, va directamente a la que está esperando el resultado.
    • ¿Y que pasa si el usuario pulsa back cuando está al final del flow?
    • Que se sale del flow.
    • Pero entonces pierde toda la información que tenía entre medias ¿no?
    • Si.
    • ¿Y si en lugar de tener un flow de 2 actividades tienes un flow de 5 o 6 o incluso de 10?
    • Pues el usuario tendría que comenzar el flow desde el principio.
    • Bien, ahí es donde quería llegar, yo también me he enfrentado a este problema y de momento, esta es la solución que mas me hag gustado.

    ¿Como hacerlo?

    Esto es bastante sencillo de conseguir y lo más importante, con poco código:
    • Primero necesitamos lanzar la primera Activity del flow con startActivityForResult:
    
        Intent intent = new Intent(this, ProfileNameStepActivity.class);
        startActivityForResult(intent, PROFILE_REQUEST_CODE_FLOW);
        
    
    • Y esperamos el resultado como hariamos si el flow tuviese una sola Activity.
    
      @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);
        }
      }
      
    
    • También en la última Activity del flow añadiremos:
    
        Intent intent = new Intent();
        intent.putExtra(ExtrasUtils.EXTRA_PROFILE,
            new Profile.Builder().withName(name).withAddress(address).build());
        setResult(RESULT_OK, intent);
        finish();
    
    
    De momento no hemos hecho nada especial, esté código lo hemos escrito cientos de veces cada vez que lanzamos una Activityesperando un resultado de ella.
    Esta es la parte interesante...
    • Ahora lanzamos el resto de actividades del flow con startActivityForResult también, aunque en este caso el requestCode no es importante ya que no esperamos su resultado:
    
          /*
          Podemos pasar cualquier número como request code aquí porque no lo escucharémos 
          (a no ser que queramos lanzar un sub-flow, después explico que sería un sub-flow y 
          como incluirlo)
          */
          Intent intent = new Intent(this, ProfileFinalStepActivity.class);
          startActivityForResult(intent, 0);
    
    
    • Finalmente necesitamos esta pieza de código en nuestra BaseActivity de la cual heredan todas nuestras actividades.
    
      @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        
        if (resultCode == RESULT_OK) {
          setResult(resultCode, data);
          finish();
        }
      }
    
    
    Esta última parte de código, es la que realmente hace la magia. Si te fijas, como hemos lanzado todas nuestras actividades con startActivityForResult, cuando estas terminen la Activity anterior será llamada en onActivityResult, y lo que estamos haciendo es comprobar si la Activity de la que viene ha añadido el resultado ya, si lo ha hecho, pasamos el resultado a la Activity anterior y esto se hará progresivamente hasta que alguna Activity haya sobreescrito el método onActivityResult y esté actualmente esperando por el requestCode.
    Está misma técnica puede ser utilizada para incluir sub-flows dentro de un flow, imagina por ejemplo que tu aplicación permite al usuario realizar compras y que para realizar esa compra el usuario tiene que proveer alguna información básica como el nombre y la dirección (o un perfil entero).
    El mismo código que hemos utilizado para empezar/acabar un flow, podemos incluirlo en cualquier paso del flow para añadir un sub-flow y funcionará exactamente igual.

    ¿Que hemos conseguido con esto?

    • Ser capaz de empezar y acabar un flow en el mismo sitio.
    • Entregar el resultado de un flow (grupo de actividades) de una forma sencilla.
    • Posibilidad de añadir flows adicionales dentro del flow.

No hay comentarios:

Publicar un comentario