tutorial recyclerview peticiones implementar ejemplo android web-services rest retrofit

android - recyclerview - ¿Práctica recomendada para implementar la devolución de llamada de Retrofit a la actividad recreada?



retrofit post (9)

Estoy cambiando a Retrofit e intentando comprender la arquitectura adecuada para usarla con devoluciones de llamada asíncronas.

Por ejemplo, tengo una interfaz:

interface RESTService{ @GET("/api/getusername") void getUserName(@Query("user_id") String userId, Callback<Response> callback); }

Y corro esto desde la actividad principal:

RestAdapter restAdapter = new RestAdapter.Builder() .setServer("WEBSITE_URL") .build(); RESTService api = restAdapter.create(RESTService.class); api.getUserName(userId, new Callback<Response> {...});

Luego el usuario gira el dispositivo y tengo actividad recién creada ... ¿Qué pasó aquí? ¿Cómo puedo obtener respuesta a la nueva actividad (supongo que la llamada de API en segundo plano se ejecutará más tiempo que la primera actividad). Tal vez debo usar instancia estática de devolución de llamada o qué? Por favor muéstrame el camino correcto ...



En primer lugar, su actividad se filtra aquí porque esta línea: api.getUserName (userId, nueva devolución de llamada {...}) crea una clase de devolución de llamada anónima que contiene una fuerte referencia a su actividad principal. Cuando se gira el dispositivo antes de llamar a la Devolución de llamada, entonces la actividad principal no se recogerá. Dependiendo de lo que haga en Callback.call (), su aplicación puede generar un comportamiento indefinido.

La idea general para manejar tales escenarios es:

  1. Nunca crees una clase interna no estática (o una clase anónima como se menciona en el problema).
  2. En su lugar, cree una clase estática que contenga una WeakReference <> a la Actividad / Fragmento.

Lo anterior solo evita fugas. Todavía no te ayuda a recuperar la llamada de Retrofit a tu Actividad.

Ahora, para que los resultados vuelvan a su componente (Actividad en su caso), incluso después del cambio de configuración, puede usar un fragmento retenido sin cabeza adjunto a su Actividad, que realiza la llamada a Retrofit. Lea más aquí acerca de Fragmento retenido - http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)

La idea general es que el Fragmento se adhiere automáticamente a la Actividad en el cambio de configuración.


Esta es una limitación típica de este tipo de soluciones, por defecto Android crea una nueva actividad cuando el usuario gira el dispositivo.

Sin embargo, hay una solución fácil para este caso, configure el oyente de orientación. Una vez hecho esto, Android no creará una nueva actividad.

<activity ... android:configChanges="orientation" />

Otra forma de solucionarlo es mediante el uso de la modificación con otros marcos como Robospice.


He estado usando una especie de implementación de MVP (ModelViewPresenter) en mis aplicaciones de Android. Para la solicitud de modificación realicé las llamadas de actividad, es el presentador respectivo, que a su vez realiza la solicitud de modificación y, como parámetro, envío una devolución de llamada con un oyente personalizado adjunto (implementado por el presentador). Cuando la devolución de llamada llega a los métodos de onSuccess o onFailure , llamo a los métodos respectivos del oyente, que llama al presentador y luego a los métodos de actividad: P

Ahora, en caso de que se gire la pantalla, cuando mi actividad se vuelva a crear, se adjuntará al presentador. Esto se realiza mediante una implementación personalizada de la aplicación de Android, donde guarda la instancia de los presentadores y utiliza un mapa para recuperar el presentador correcto de acuerdo con la clase de la actividad.

No sé si es la mejor manera, quizás la respuesta @pareshgoel sea mejor, pero me ha funcionado: D

Ejemplos:

public abstract interface RequestListener<T> { void onSuccess(T response); void onFailure(RetrofitError error); }

...

public class RequestCallback<T> implements Callback<T> { protected RequestListener<T> listener; public RequestCallback(RequestListener<T> listener){ this.listener = listener; } @Override public void failure(RetrofitError arg0){ this.listener.onFailure(arg0); } @Override public void success(T arg0, Response arg1){ this.listener.onSuccess(arg0); } }

Implemente el oyente en algún lugar del presentador y, en los métodos de anulación, llame al método de un presentador que realizará la llamada a la Actividad. Y llame a donde quiera en el presentador para iniciar todo: P

Request rsqt = restAdapter.create(Request.class); rsqt.get(new RequestCallback<YourExpectedObject>(listener));

Espero que te ayude


Le recomiendo que vea este video en Google I / O.

Habla sobre cómo crear solicitudes REST delegándolas a un servicio (que casi nunca se elimina). Cuando la solicitud se completa, se almacena inmediatamente en la base de datos incorporada de Android para que los datos estén disponibles inmediatamente cuando su actividad esté lista.

Con este enfoque, nunca tendrá que preocuparse por el ciclo de vida de la actividad y sus solicitudes se manejarán de una manera mucho más desacoplada.

El video no habla específicamente sobre la retroadaptación, pero puedes adaptar fácilmente la adaptación para este paradigma.


Para posibles llamadas de larga duración al servidor, uso AsyncTaskLoader . Para mí, la principal ventaja de los cargadores es el manejo del ciclo de vida de la actividad. onLoadFinished solo se invoca si su actividad es visible para el usuario. Los cargadores también se comparten entre actividad / fragmento y cambios de orientación.

Así que creé un ApiLoader que usa modificaciones de llamadas sincrónicas en loadInBackground .

abstract public class ApiLoader<Type> extends AsyncTaskLoader<ApiResponse<Type>> { protected ApiService service; protected ApiResponse<Type> response; public ApiLoader(Context context) { super(context); Vibes app = (Vibes) context.getApplicationContext(); service = app.getApiService(); } @Override public ApiResponse<Type> loadInBackground() { ApiResponse<Type> localResponse = new ApiResponse<Type>(); try { localResponse.setResult(callServerInBackground(service)); } catch(Exception e) { localResponse.setError(e); } response = localResponse; return response; } @Override protected void onStartLoading() { super.onStartLoading(); if(response != null) { deliverResult(response); } if(takeContentChanged() || response == null) { forceLoad(); } } @Override protected void onReset() { super.onReset(); response = null; } abstract protected Type callServerInBackground(SecondLevelApiService api) throws Exception; }

En tu actividad, inicias este cargador de la siguiente manera:

getSupportLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks<ApiResponse<DAO>>() { @Override public Loader<ApiResponse<DAO>> onCreateLoader(int id, Bundle args) { spbProgress.setVisibility(View.VISIBLE); return new ApiLoader<DAO>(getApplicationContext()) { @Override protected DAO callServerInBackground(ApiService api) throws Exception { return api.requestDAO(); } }; } @Override public void onLoadFinished(Loader<ApiResponse<DAO>> loader, ApiResponse<DAO> data) { if (!data.hasError()) { DAO dao = data.getResult(); //handle data } else { Exception error = data.getError(); //handle error } } @Override public void onLoaderReset(Loader<ApiResponse<DAO>> loader) {} });

Si desea solicitar datos varias veces, utilice restartLoader en lugar de initLoader .


Usa Robospice

Todos los componentes en su aplicación que requieren datos, regístrese con el servicio de especias. El servicio se encarga de enviar su solicitud al servidor (a través de la actualización si lo desea). Cuando vuelve la respuesta, todos los componentes registrados se notifican. Si hay uno de ellos no disponible más (como una actividad que recibió una patada debido a la rotación), simplemente no se notifica.

Beneficio: una sola solicitud que no se pierde, sin importar si gira el dispositivo, abre nuevos diálogos / fragmentos, etc.


Usando Retrofit2 para manejar el cambio de orientación. Me preguntaron esto en una entrevista de trabajo y me rechazaron por no saberlo en ese momento, pero aquí está ahora.

public class TestActivity extends AppCompatActivity { Call<Object> mCall; @Override public void onDestroy() { super.onDestroy(); if (mCall != null) { if (mCall.isExecuted()) { //An attempt will be made to cancel in-flight calls, and // if the call has not yet been executed it never will be. mCall.cancel(); } } } }