Ich wechsle zu Retrofit und versuche, die richtige Architektur für die Verwendung mit asynchronen Rückrufen zu verstehen.
Zum Beispiel habe ich eine Schnittstelle:
interface RESTService{
@GET("/api/getusername")
void getUserName(@Query("user_id") String userId,
Callback<Response> callback);
}
Und ich führe dies von der Hauptaktivität aus:
RestAdapter restAdapter = new RestAdapter.Builder()
.setServer("WEBSITE_URL")
.build();
RESTService api = restAdapter.create(RESTService.class);
api.getUserName(userId, new Callback<Response> {...});
Dann dreht der Benutzer das Gerät und ich habe eine neu erstellte Aktivität ... Was war hier passiert? Wie kann ich eine Antwort auf die neue Aktivität erhalten (ich gehe davon aus, dass der API-Aufruf im Hintergrund länger ausgeführt wird als das erste Aktivitätsleben). Vielleicht muss ich eine statische Instanz des Rückrufs verwenden oder was? Bitte zeig mir den richtigen Weg ...
quelle
setRetainInstance
undObservable.cache
dafür brauchen . Hier ist ein Beispiel, das ich geschrieben habe und das so ziemlich ein RxAndroid-Beispiel kopiert: github.com/ber4444/u2020-v2/blob/master/Application/src/main/…Für potenziell lange laufende Serveraufrufe verwende ich einen AsyncTaskLoader . Für mich ist der Hauptvorteil von Loadern die Handhabung des Aktivitätslebenszyklus. onLoadFinished wird nur aufgerufen, wenn Ihre Aktivität für den Benutzer sichtbar ist. Lader werden auch zwischen Aktivitäts- / Fragment- und Orientierungsänderungen geteilt.
Also habe ich einen ApiLoader erstellt, der synchrone Aufrufe in loadInBackground nachrüstet .
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; }
In Ihrer Aktivität initiieren Sie diesen Loader wie folgt:
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) {} });
Wenn Sie Daten mehrmals anfordern möchten, verwenden Sie restartLoader anstelle von initLoader .
quelle
Service
oderIntentService
stattAsyncTask
?Ich habe eine Art MVP-Implementierung (ModelViewPresenter) für meine Android-Apps verwendet. Für die Nachrüstanforderung habe ich die Aktivitätsaufrufe des jeweiligen Präsentators getätigt, wodurch wiederum die Nachrüstanforderung ausgeführt wird, und als Parameter sende ich einen Rückruf mit einem daran angehängten benutzerdefinierten Listener (vom Präsentator implementiert). Wenn der Rückruf erreicht
onSuccess
oderonFailure
Methoden, rufe ich die jeweiligen Methoden des Listeners auf, die den Präsentator und dann die Aktivitätsmethoden aufrufen: P.Wenn der Bildschirm jetzt gedreht wird und meine Aktivität neu erstellt wird, hängt sie sich selbst an den Präsentator an. Dies erfolgt mithilfe einer benutzerdefinierten Implementierung der Android-Anwendung, in der die Instanz des Präsentators gespeichert wird, und mithilfe einer Karte zum Wiederherstellen des richtigen Präsentators gemäß der Klasse der Aktivität.
Ich weiß nicht, ob es der beste Weg ist, vielleicht ist die Antwort von @pareshgoel besser, aber es hat bei mir funktioniert: D.
Beispiele:
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); } }
Implementieren Sie den Listener irgendwo auf dem Präsentator, und rufen Sie auf den überschriebenen Methoden die Methode eines Präsentators auf, mit der die Aktivität aufgerufen wird. Und rufen Sie den Moderator an, wo immer Sie möchten, um alles zu initiieren: P.
Request rsqt = restAdapter.create(Request.class); rsqt.get(new RequestCallback<YourExpectedObject>(listener));
Hoffe es hilft dir.
quelle
Erstens leckt Ihre Aktivität hier, weil diese Zeile: api.getUserName (Benutzer-ID, neuer Rückruf {...}) eine anonyme Rückrufklasse erstellt, die einen starken Verweis auf Ihre MainActivity enthält. Wenn das Gerät gedreht wird, bevor der Rückruf aufgerufen wird, wird die MainActivity nicht durch Müll gesammelt. Abhängig davon, was Sie in Callback.call () tun, kann Ihre App zu undefiniertem Verhalten führen.
Die allgemeine Idee, mit solchen Szenarien umzugehen, lautet:
Das Obige verhindert nur Lecks. Es hilft Ihnen immer noch nicht, den Retrofit-Rückruf zu Ihrer Aktivität zurückzubekommen.
Um die Ergebnisse auch nach einer Konfigurationsänderung wieder in Ihre Komponente (in Ihrem Fall Aktivität) zu übertragen, möchten Sie möglicherweise ein kopfloses, beibehaltenes Fragment verwenden, das an Ihre Aktivität angehängt ist und Retrofit aufruft. Lesen Sie hier mehr über Retained Fragment - http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)
Die allgemeine Idee ist, dass sich das Fragment bei Konfigurationsänderungen automatisch an die Aktivität anfügt.
quelle
Ich empfehle Ihnen dringend , dieses Video bei Google I / O anzusehen .
Es wird erläutert, wie REST-Anforderungen erstellt werden, indem sie an einen Dienst delegiert werden (der fast nie beendet wird). Wenn die Anforderung abgeschlossen ist, wird sie sofort in der integrierten Datenbank von Android gespeichert, sodass die Daten sofort verfügbar sind, wenn Ihre Aktivität fertig ist.
Mit diesem Ansatz müssen Sie sich nie um den Lebenszyklus der Aktivität kümmern, und Ihre Anforderungen werden viel entkoppelter behandelt.
In dem Video geht es nicht speziell um Nachrüstung, aber Sie können die Nachrüstung leicht an dieses Paradigma anpassen.
quelle
Verwenden Sie Robospice
Alle Komponenten in Ihrer App, die Daten benötigen, registrieren sich beim Gewürzdienst. Der Dienst kümmert sich um das Senden Ihrer Anfrage an den Server (auf Wunsch per Nachrüstung). Wenn die Antwort zurückkommt, werden alle registrierten Komponenten benachrichtigt. Wenn einer von ihnen nicht mehr verfügbar ist (wie eine Aktivität, die aufgrund einer Rotation gekickt wurde), wird sie einfach nicht benachrichtigt.
Vorteil: Eine einzige Anfrage, die nicht verloren geht, egal ob Sie Ihr Gerät drehen, neue Dialoge / Fragmente öffnen usw.
quelle
Verwenden von Retrofit2 zur Behandlung von Orientierungsänderungen. Ich wurde in einem Vorstellungsgespräch danach gefragt und abgelehnt, weil ich es damals nicht wusste, aber hier ist es jetzt.
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(); } } } }
quelle