Android – Llamadas de red asíncronas – Respuesta dependiente entre sí

Acabo de encontrar este tipo de situación mientras desarrollaba una aplicación de Android hoy en donde se me requirió hacer gráficos basados ​​en respuestas de 2 API diferentes. Estoy usando Volley y lo que hice fue hacer una llamada de red secuencial, es decir, hice la primera solicitud, y en el método onResponse de esa solicitud hice la segunda solicitud. Y luego vuelvo la vista (el gráfico) en el método onResponse de la segunda solicitud.

Ahora quiero optimizar esta situación. Quiero saber una manera que puedo hacer estas 2 llamadas de la red asincrónicamente donde hago la visión solamente después de recibir respuestas de ambas API. Por lo tanto, digamos que tengo 3 métodos modulares a saber –

  1. GetDataFromServer1 (llamada de red para obtener datos de un servidor)
  2. GetDataFromServer2 (llamada de red para obtener datos de otro servidor)
  3. loadView (renderizar gráficos basados ​​en datos recibidos de 2 llamadas de red)

¿Cómo lo hago? ¿Puede alguien arrojar algo de luz sobre él?

    4 Solutions collect form web for “Android – Llamadas de red asíncronas – Respuesta dependiente entre sí”

    La solución @tommus es el mejor enfoque.


    Si desea ir con un enfoque de código simple o menos, puede utilizar un indicador booleano para asegurarse de que ambos se ejecutan y se mueven hacia adelante en función de la condición.

    Declare una variable booleana volátil que se utilizará como indicador.

     private volatile boolean flag = false; 

    La bandera sería falsa al comienzo. Ahora, haga una llamada a ambos servicios web. Cualquier servicio que se ejecute activará este indicador VERDADERO.

     getDataFromServer1(); function void onCompleteServer1() { if(flag) { loadViews(); } else { flag = true; } } getDataFromServer2(); onCompleteServer2Request() { if(flag) { loadViews(); } else { flag = true; } } 

    Versión 1 – con bibliotecas externas

    Este es un ejemplo perfecto donde RxAndroid viene práctico (o más general – cualquier marco que apoya la programación impulsada por eventos).


    Digamos que tenemos las siguientes clases de dominio que nos permiten obtener algunos datos de los servicios web:

    Clase de repositorio:

     public class Repository { protected String name; public Repository(String name) { this.name = name; } public String getName() { return name; } } 

    Interfaz de servicio:

     public interface GitService { List<Repository> fetchRepositories(); } 

    Primera implementación del servicio:

     public class BitbucketService implements GitService { @Override public List<Repository> fetchRepositories() { // Time consuming / IO consuming task. try { Thread.sleep(2000); } catch (InterruptedException e) { // Swallow exception. } List<Repository> result = new ArrayList<>(); result.add(new Repository("http://some-fancy-repository.com/")); return result; } } 

    Implementación del segundo servicio:

     public class GithubService implements GitService { @Override public List<Repository> fetchRepositories() { // Time consuming / IO consuming task. try { Thread.sleep(2000); } catch (InterruptedException e) { // Swallow exception. } List<Repository> result = new ArrayList<>(); result.add(new Repository("http://some-fancier-repository.com/")); return result; } } 

    Teniendo encima de nosotros podemos crear fácilmente un observable (un objeto que mira si algo había sucedido) que comprueba si hemos descargado con éxito datos de ambos servicios. Esta responsabilidad tiene el siguiente método:

     public Observable<List<Repository>> fetchRepositories() { // This is not the best place to instantiate services. GitService github = new GithubService(); GitService bitbucket = new BitbucketService(); return Observable.zip( Observable.create(subscriber -> { subscriber.onNext(github.fetchRepositories()); }), Observable.create(subscriber -> { subscriber.onNext(bitbucket.fetchRepositories()); }), (List<Repository> response1, List<Repository> response2) -> { List<Repository> result = new ArrayList<>(); result.addAll(response1); result.addAll(response2); return result; } ); } 

    Lo único que hay que hacer es ejecutar la tarea en algún lugar (ejemplo en el método onCreate ):

     @Override protected void onCreate(Bundle savedInstanceState) { (...) AndroidObservable .bindActivity(this, fetchRepositories()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(repos -> showRepositories(repos), error -> showErrors()); } 

    El código de arriba es un lugar donde ocurre la magia. Aquí está:

    • Definido un contexto de la tarea,
    • Definió una necesidad de crear hilos asincrónicos,
    • Define que el resultado debe ser manejado en el hilo de interfaz de usuario cuando el trabajo duro termina,
    • Define qué métodos llamar para manejar resultados y errores.

    Arriba, en subscribe estamos pasando llamadas lambda para mostrar resultados / errores al usuario:

     private void showRepositories(List<Repository> repositories) { // Show repositories in your fragment. } private void showErrors() { // Pops up some contextual information / help. } 

    Como Android utiliza actualmente SDK 1.7 es necesario utilizar una biblioteca que nos permite usar lambdas en el código 1.7-compliant. Personalmente, estoy usando retrolambda para este caso.

    Si no te gusta lambdas – siempre tienes la posibilidad de implementar clases anónimas siempre que sea necesario.

    De esta manera podemos evitar escribir un montón de código de la placa de la caldera de Android.


    Versión 2: sin bibliotecas externas

    Si no desea utilizar bibliotecas externas puede lograr similares con AsyncTasks acompañado por Executor .


    Reutilizaremos las clases de dominio descritas anteriormente: Repository , GitService , GithubService y BitbucketService .

    Como queremos encuestar cuántas tareas se han terminado, vamos a introducir algún tipo de contador en nuestra actividad:

     private AtomicInteger counter = new AtomicInteger(2); 

    Compartiremos este objeto en nuestras tareas asíncronas.

    A continuación, tenemos que implementar una tarea en sí, por ejemplo, como esto:

     public class FetchRepositories extends AsyncTask<Void, Void, List<Repository>> { private AtomicInteger counter; private GitService service; public FetchRepositories(AtomicInteger counter, GitService service) { this.counter = counter; this.service = service; } @Override protected List<Repository> doInBackground(Void... params) { return service.fetchRepositories(); } @Override protected void onPostExecute(List<Repository> repositories) { super.onPostExecute(repositories); int tasksLeft = this.counter.decrementAndGet(); if(tasksLeft <= 0) { Intent intent = new Intent(); intent.setAction(TASKS_FINISHED_ACTION); sendBroadcast(intent); } } } 

    Esto es lo que ha sucedido:

    • En el constructor hemos inyectado el contador compartido y el servicio que se utilizó para obtener datos,
    • En el método doInBackground hemos delegado el control a nuestro servicio dedicado,
    • onPostExecute método que hemos probado si todas las tareas previstas ha terminado,
    • Después de todo el trabajo duro – se ha enviado una emisión a la actividad.

    Luego tenemos que recibir una emisión potencial que nos informe que el trabajo se ha hecho. Para ese caso hemos implementado receptor de radiodifusión:

     public class FetchBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.d("onReceive", "All tasks has been finished."); // No need to test // if intent.getAction().equals(TASKS_FINISHED_ACTION) {} // as we used filter. } } 

    En lugar de registrar el mensaje, debe actualizar su vista.

    Mencionado constante TASKS_FINISHED_ACTION nombres de su filtro:

      private static final String TASKS_FINISHED_ACTION = "some.intent.filter.TASKS_FINISHED"; 

    Recuerde inicializar y registrar el receptor y los filtros en ambos – su actividad y manifiesto.

    Actividad:

     private BroadcastReceiver receiver = new FetchBroadcastReceiver(); @Override protected void onResume() { super.onResume(); IntentFilter filter = new IntentFilter(); filter.addAction(TASKS_FINISHED_ACTION); registerReceiver(receiver, filter); } 

    Manifiesto (dentro de la etiqueta de aplicación):

     <receiver android:name=".TestActivity$FetchBroadcastReceiver"/> 

    Puse la clase del receptor como public en TestActivity así que parece extraño.

    En el manifiesto también tiene que registrar su acción (dentro del filtro de intención de actividad):

     <action android:name="some.intent.filter.TASKS_FINISHED"/> 

    Recuerde anular el registro de su receptor en el método onPause() .


    Una vez preparada la actividad, puedes ejecutar tus tareas en algún lugar (por ejemplo, en el método onCreate como en el primer ejemplo):

     if(Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) { new FetchRepositories(counter, new GithubService()) .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new FetchRepositories(counter, new BitbucketService()) .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } else { // Below Honeycomb there was no parallel tasks. new FetchRepositories(counter, new GithubService()).execute(); new FetchRepositories(counter, new BitbucketService()).execute(); } 

    Como se puede notar, las tareas paralelas se ejecutará sólo en Honeycomb y superiores. Antes de que esta versión del grupo de subprocesos de Android pueda contener hasta 1 tarea.


    Por lo menos hemos utilizado algunos patrones de la inyección de la dependencia y de la estrategia. 🙂

    No Perfecto Pero espero que esta lógica funcione para usted

      onDatafromServer1Fetched{ flag1 = true; } onDataFromServer2Fetched{ flag2=true; } main(){ boolean flag1 = false; boolean flag2 =false; getDataFromSerVer1(); getDataFromServer2(); while(!flag1 && !flag2){/**no code required here*/} loadView(); } 

    Si la solicitud dos tiene el mismo formato de datos, y mantener por el mismo contenedor. ¿Por qué no comprobar el tamaño del recipiente simplemente.

     public class MainActivity extends AppCompatActivity { private List<Data> myList = new ArrayList<>(); private boolean error; // if one request got error, another one need to konw, //use to handle response size 0 or other error @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); GsonRequest request1 = new GsonRequest(params... , this.mClazz, null, new Response.Listener() { @Override public void onResponse(Object response) { boolean updateUI = false; if(myList.size()>0){ //or > the init size updateUI = true; } myList.addAll(response); if(updateUI){ notifydatasetchange(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); GsonRequest request2 = new GsonRequest(params... , this.mClazz, null, new Response.Listener() { @Override public void onResponse(Object response) { boolean updateUI = false; if(myList.size()>0){ //or > the init size updateUI = true; } myList.addAll(response); if(updateUI){ notifydatasetchange(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); } 

    }

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.