webentwicklung-frage-antwort-db.com.de

Best Practice für die Implementierung eines Retrofit-Rückrufs für neu erstellte Aktivitäten?

Ich wechsele 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 aus der Hauptaktivität heraus 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 neu Aktivität erstellt ... Was war hier passiert? Wie kann ich auf die neue Aktivität reagieren (ich gehe davon aus, dass der API-Aufruf im Hintergrund länger ausgeführt wird als die erste Aktivität)? Vielleicht muss ich eine statische Instanz des Rückrufs verwenden oder was? Bitte zeigen Sie mir den richtigen Weg ...

72
lordmegamax

Verwenden Sie otto . Es gibt viele Beispiele zum Mischen von Otto und zum Nachrüsten, zum Beispiel https://github.com/pat-dalberg/ImageNom/blob/master/src/com/dalberg/Android/imagenom /async/FlickrClient.Java

Oder lesen Sie diesen Beitrag http://www.mdswanson.com/blog/2014/04/07/durable-Android-rest-clients.html It Antworten auf fast alle Fragen

42
avgx

Für potenziell lange laufende Serveraufrufe verwende ich ein AsyncTaskLoader . Der Hauptvorteil von Loadern ist für mich die Handhabung des Aktivitätslebenszyklus. onLoadFinished wird nur aufgerufen, wenn Ihre Aktivität für den Benutzer sichtbar ist. Die Lader werden auch für Aktivitäts-/Fragment- und Ausrichtungsänderungen verwendet.

Also habe ich einen ApiLoader erstellt, der nachträglich synchrone Aufrufe in loadInBackground verwendet.

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 starten 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 .

34
Benjamin

Ich habe eine Art MVP (ModelViewPresenter) -Implementierung für meine Android= Apps verwendet. Für die Nachrüstanforderung habe ich die Activity-Aufrufe an den jeweiligen Presenter gesendet, der wiederum die Nachrüstanforderung und als parameter Ich sende einen Callback mit einem benutzerdefinierten Listener (vom Presenter implementiert) Wenn der Callback die Methoden onSuccess oder onFailure erreicht, rufe ich die entsprechenden Methoden des Listeners auf, die den Presenter und dann aufrufen die Aktivitätsmethoden: P

Wenn nun der Bildschirm gedreht wird und meine Aktivität neu erstellt wird, hängt sie sich an den Presenter an. Dies erfolgt mithilfe einer benutzerdefinierten Implementierung der Android-Anwendung, in der die Instanz der Präsentatoren gespeichert wird, und mithilfe einer Karte, mit der der richtige Präsentator gemäß der Klasse der Aktivität wiederhergestellt wird.

Ich weiß nicht, ob es der beste Weg ist, vielleicht @pareshgoel Antwort ist besser, aber es hat für mich 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 Presenter, und rufen Sie auf den overrode-Methoden eine Presenter-Methode auf, die den Aufruf der Activity vornimmt. Und rufen Sie den Moderator an, wo immer Sie möchten: P

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

Hoffe es hilft dir.

21
LeoFarage

Erstens tritt hier eine Undichtigkeit Ihrer Aktivität auf, da in der folgenden Zeile: api.getUserName (userId, new Callback {...}) eine anonyme Callback-Klasse erstellt wird, 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 überflüssig gesammelt. Abhängig davon, was Sie in Callback.call () tun, kann Ihre App undefiniertes Verhalten hervorrufen.

Die allgemeine Idee, solche Szenarien zu handhaben, ist:

  1. Erstellen Sie niemals eine nicht statische innere Klasse (oder eine anonyme Klasse, wie im Problem erwähnt).
  2. Erstellen Sie stattdessen eine statische Klasse, die eine WeakReference <> für die Aktivität/das Fragment enthält.

Das oben Genannte verhindert nur Lecks. Es hilft Ihnen immer noch nicht, den Retrofit-Aufruf zu Ihrer Aktivität zurückzubringen.

Damit die Ergebnisse auch nach einer Konfigurationsänderung wieder in Ihrer Komponente (in Ihrem Fall "Aktivität") angezeigt werden, können Sie ein kopfloses Fragment verwenden, das an Ihre Aktivität angehängt ist und das 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.

10
pareshgoel

Ich kann Ihnen nur empfehlen sehen Sie sich dieses Video bei Google I/O an .

Es geht darum, wie Sie REST Anforderungen erstellen, indem Sie sie an einen Dienst delegieren (der fast nie beendet wird). Wenn die Anforderung abgeschlossen ist, wird sie sofort in der in Android integrierten Datenbank gespeichert, sodass die Daten gespeichert werden sofort verfügbar, wenn Ihre Aktivität fertig ist.

Mit diesem Ansatz müssen Sie sich keine Gedanken über den Lebenszyklus der Aktivität machen, und Ihre Anforderungen werden viel besser entkoppelt behandelt.

In dem Video geht es nicht speziell um die Nachrüstung, aber Sie können die Nachrüstung einfach an dieses Paradigma anpassen.

5
Martin Konecny

Mit Retrofit2 Orientierungsänderungen vornehmen. Ich wurde dies in einem Vorstellungsgespräch gefragt und abgelehnt, weil ich es zu der Zeit nicht wusste, aber jetzt ist es da.

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();
            }
        }
    }
    }
3
John61590

Als ich ein Neuling in der Nachrüstung war, habe ich den besten Weg von diesen Seiten gelernt

Api mit Nachrüstung verbrauchen

Nachrüstanleitung

2
Ameen Maheen

Verwenden Sie Robospice

Alle Komponenten in Ihrer App, die Daten benötigen, registrieren sich beim Gewürzdienst. Der Service kümmert sich um das Versenden Ihrer Anfrage an den Server (bei Bedarf per Retrofit). 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 er nur nicht benachrichtigt.

Vorteil: Eine einzige Anfrage, die nicht verloren geht, egal ob Sie Ihr Gerät drehen, neue Dialoge/Fragmente öffnen usw.

2
stoefln