Frage

ich las viel, wie mein Beispiel Zustand zu speichern, oder wie mit meiner Tätigkeit beschäftigen während Bildschirmdrehung immer zerstört.

Es scheint eine Menge Möglichkeiten, aber ich habe nicht herausgefunden, die man am besten für Ergebnisse eines AsyncTask abgerufen werden.

Ich habe einige AsyncTasks, die einfach gestartet werden, wieder und die isFinishing() Methode der Aktivität aufrufen und wenn die Aktivität Finishing sie nicht nichts aktualisieren.

Das Problem ist, dass ich eine Aufgabe, die eine Anforderung an einen Web-Service tut, die ausfallen kann oder erfolgreich zu sein und die Aufgabe, einen Neustart in einem finanziellen Verlust für den Anwender führen würde.

Wie würden Sie dieses Problem lösen? Was sind die Vor- und Nachteile der möglichen Lösungen?

War es hilfreich?

Lösung

Mein erster Vorschlag wäre, um sicherzustellen, dass Sie tatsächlich benötigen Ihre Aktivität zurückgesetzt auf einem Bildschirm-Rotation (das Standardverhalten) zu sein. Jedes Mal, wenn ich Probleme mit der Drehung hatte ich dieses Attribut auf meinen <activity>-Tag in der AndroidManifest.xml hinzugefügt habe, und war ganz gut.

android:configChanges="keyboardHidden|orientation"

Es sieht seltsam, aber was es tut, es zu Ihrem onConfigurationChanged() Methode Hand aus, wenn Sie noch kein Konto liefern es funktioniert einfach nichts anderes als Wieder messen, das Layout, die eine vollkommen ausreichend Art und Weise der Handhabung der Dreh zu sein scheint die meiste Zeit.

Andere Tipps

Sie können überprüfen, wie ich AsyncTasks und Orientierungsänderungen behandeln unter code.google.com/p/shelves . Es gibt verschiedene Möglichkeiten, es zu tun, die ich in dieser App gewählt habe, ist jede aktuell ausgeführte Task abzubrechen, speichert seinen Zustand und starten Sie einen neuen mit dem gespeicherten Zustand, wenn der neue Activity erstellt wird. Es ist einfach zu tun, es funktioniert gut und als Bonus nimmt sie Ihre Aufgaben zu stoppen, wenn der Benutzer verlässt die App.

Sie können auch onRetainNonConfigurationInstance() benutzen, um Ihre AsyncTask auf die neue Activity passieren (vorsichtig sein, nicht die vorherige Activity obwohl diese Art und Weise undicht.)

Dies ist die interessanteste Frage, die ich in Bezug auf Android gesehen habe !!! Eigentlich gesucht Ich habe bereits für die Lösung in den letzten Monaten. Sie haben noch nicht gelöst.

Seien Sie vorsichtig, einfach das Überschreiben der

android:configChanges="keyboardHidden|orientation"

Zeug ist nicht genug.

Betrachten wir den Fall, wenn der Benutzer einen Anruf empfängt, während die AsyncTask läuft. Ihre Anfrage wird bereits von Server, das verarbeitet wird, so dass die AsyncTask wird für die Antwort warten. In diesem Moment geht die App im Hintergrund, da der Telefon-App im Vordergrund gerade gekommen ist. O kann Ihre Aktivität töten, da es im Hintergrund ist.

Warum man nicht immer hält einen Verweis auf die aktuelle AsyncTask auf der von Android zur Verfügung gestellt Singleton?

Jedes Mal, wenn eine Aufgabe beginnt, auf PreExecute oder auf dem Builder definieren Sie:

((Application) getApplication()).setCurrentTask(asyncTask);

Jedes Mal, wenn es fertig ist Sie es auf null gesetzt.

So können Sie immer eine Referenz, die Sie so etwas wie, onCreate oder onResume tun können wie für Ihre spezifische Logik angeeignet:

this.asyncTaskReference = ((Application) getApplication()).getCurrentTask();

Wenn es null Sie wissen, dass derzeit noch keine läuft!

: -)

Der richtige Weg, um dies ist ein Fragment zu verwenden, um die Instanz der Asynchron-Aufgabe zu behalten, über Drehungen.

Hier ist ein Link zu sehr einfaches Beispiel macht es einfach, diese Technik in Ihre Anwendungen zu folgen zu integrieren.

https://gist.github.com/daichan4649/2480065

In Pro android 4. Autor hat eine nette Art und Weise vorschlagen, dass Sie weak reference verwenden sollten.

Weak Referenznote

Zu meiner Sicht ist es besser, zu speichern AsyncTask über onRetainNonConfigurationInstance es von dem aktuellen Aktion Objekt Entkopplung und es zu einem neuen Aktivitätsobjekt nach der Orientierungsänderung verbindlich. Hier ich ein sehr schönes Beispiel gefunden, wie man die Arbeit mit AsyncTask und ProgressDialog.

Android: Hintergrundverarbeitung / Async Opeartion mit Konfigurationsänderung

Um die Zustände async opeartion während des Hintergrundprozesses zu halten: Sie können eine Hilfe Fragmente nehmen.

Sehen Sie die folgenden Schritte:

Schritt 1: a. Headerless Fragment erstellen kann Hintergrundaufgabe sagen und eine private async Taskklasse mit darin fügt

Schritt 2 (Optional) Schritt: Wenn Sie einen Laden Cursor auf Ihre Aktivität bei Temperaturen unter Code setzen:

Schritt 3: In Ihrer Hauptaktivität implementieren BackgroundTaskCallbacks in Schritt definiert Schnittstelle 1

class BackgroundTask extends Fragment {
public BackgroundTask() {

}

// Add a static interface 

static interface BackgroundTaskCallbacks {
    void onPreExecute();

    void onCancelled();

    void onPostExecute();

    void doInBackground();
}

private BackgroundTaskCallbacks callbacks;
private PerformAsyncOpeation asyncOperation;
private boolean isRunning;
private final String TAG = BackgroundTask.class.getSimpleName();

/**
 * Start the async operation.
 */
public void start() {
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION ENTER *********");
    if (!isRunning) {
        asyncOperation = new PerformAsyncOpeation();
        asyncOperation.execute();
        isRunning = true;
    }
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION EXIT *********");
}

/**
 * Cancel the background task.
 */
public void cancel() {
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION ENTER *********");
    if (isRunning) {
        asyncOperation.cancel(false);
        asyncOperation = null;
        isRunning = false;
    }
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION EXIT *********");
}

/**
 * Returns the current state of the background task.
 */
public boolean isRunning() {
    return isRunning;
}

/**
 * Android passes us a reference to the newly created Activity by calling
 * this method after each configuration change.
 */
public void onAttach(Activity activity) {
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH ENTER *********");
    super.onAttach(activity);
    if (!(activity instanceof BackgroundTaskCallbacks)) {
        throw new IllegalStateException(
                "Activity must implement the LoginCallbacks interface.");
    }

    // Hold a reference to the parent Activity so we can report back the
    // task's
    // current progress and results.
    callbacks = (BackgroundTaskCallbacks) activity;
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH EXIT *********");
}

public void onCreate(Bundle savedInstanceState) {
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE ENTER *********");
    super.onCreate(savedInstanceState);
    // Retain this fragment across configuration changes.
    setRetainInstance(true);
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE EXIT *********");
}

public void onDetach() {
    super.onDetach();
    callbacks = null;
}

private class PerformAsyncOpeation extends AsyncTask<Void, Void, Void> {
    protected void onPreExecute() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPreExecute();
        }
        isRunning = true;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE EXIT *********");
    }

    protected Void doInBackground(Void... params) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND ENTER *********");
        if (callbacks != null) {
            callbacks.doInBackground();
        }
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND EXIT *********");
        return null;
    }

    protected void onCancelled() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL ENTER *********");
        if (callbacks != null) {
            callbacks.onCancelled();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL EXIT *********");
    }

    protected void onPostExecute(Void ignore) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPostExecute();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE EXIT *********");
    }
}

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    setRetainInstance(true);
}

public void onStart() {
    super.onStart();
}

public void onResume() {
    super.onResume();
}

public void onPause() {
    super.onPause();
}

public void onStop() {
    super.onStop();
}

public class ProgressIndicator extends Dialog {

public ProgressIndicator(Context context, int theme) {
    super(context, theme);
}

private ProgressBar progressBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.progress_indicator);
    this.setCancelable(false);
    progressBar = (ProgressBar) findViewById(R.id.progressBar);
    progressBar.getIndeterminateDrawable().setColorFilter(R.color.DarkBlue, android.graphics.PorterDuff.Mode.SCREEN);
}

@Override
public void show() {
    super.show();
}

@Override
public void dismiss() {
    super.dismiss();
}

@Override
public void cancel() {
    super.cancel();
}

public class MyActivity extends FragmentActivity implements BackgroundTaskCallbacks,{

private static final String KEY_CURRENT_PROGRESS = "current_progress";

ProgressIndicator progressIndicator = null;

private final static String TAG = MyActivity.class.getSimpleName();

private BackgroundTask task = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(//"set your layout here");
    initialize your views and widget here .............



    FragmentManager fm = getSupportFragmentManager();
    task = (BackgroundTask) fm.findFragmentByTag("login");

    // If the Fragment is non-null, then it is currently being
    // retained across a configuration change.
    if (task == null) {
        task = new BackgroundTask();
        fm.beginTransaction().add(task, "login").commit();
    }

    // Restore saved state
    if (savedInstanceState != null) {
        Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON CREATE :: "
                + task.isRunning());
        if (task.isRunning()) {
            progressIndicator = new ProgressIndicator(this,
                    R.style.TransparentDialog);
            if (progressIndicator != null) {
                progressIndicator.show();
            }
        }
    }
}

@Override
protected void onPause() {
    // TODO Auto-generated method stub
    super.onPause();

}

@Override
protected void onSaveInstanceState(Bundle outState) {
    // save the current state of your operation here by saying this 

    super.onSaveInstanceState(outState);
    Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON SAVE INSTANCE :: "
            + task.isRunning());
    outState.putBoolean(KEY_CURRENT_PROGRESS, task.isRunning());
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}


private void performOperation() {

            if (!task.isRunning() && progressIndicator == null) {
                progressIndicator = new ProgressIndicator(this,
                        R.style.TransparentDialog);
                progressIndicator.show();
            }
            if (task.isRunning()) {
                task.cancel();
            } else {
                task.start();
            }
        }


@Override
protected void onDestroy() {
    super.onDestroy();
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}

@Override
public void onPreExecute() {
    Log.i(TAG, "CALLING ON PRE EXECUTE");
}

@Override
public void onCancelled() {
    Log.i(TAG, "CALLING ON CANCELLED");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();

}

public void onPostExecute() {
    Log.i(TAG, "CALLING ON POST EXECUTE");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
        progressIndicator = null;
    }
}

@Override
public void doInBackground() {
    // put your code here for background operation
}

}

Eine Sache zu prüfen ist, ob das Ergebnis der AsyncTask auf die Aktivität nur zur Verfügung stehen soll, der die Aufgabe gestartet. Wenn ja, dann ist Romain Guy Antwort besten. Wenn es mit anderen Aktivitäten Ihrer Anwendung zur Verfügung stehen soll, dann in onPostExecute können Sie LocalBroadcastManager verwenden.

LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("finished"));

Sie werden auch sicher brauchen, um die Aktivität Situation richtig behandelt, wenn Broadcast gesendet wird, während die Aktivität angehalten wird.

Haben Sie einen Blick auf diese Post . Dieser Beitrag beinhaltet AsyncTask Durchführung langer Betrieb und Speicherverlust laufen, wenn Bildschirmdrehung geschieht in beide eine Beispielanwendung. Die Probe App ist verfügbar auf der Quelle Schmiede

Meine Lösung.

In meinem Fall hat sich eine Kette von AsyncTasks mit dem gleichen Kontext bekommt. Aktivität hatte einen Zugriff nur auf erste. alle laufenden Task abzubrechen habe ich die folgenden:

public final class TaskLoader {

private static AsyncTask task;

     private TaskLoader() {
         throw new UnsupportedOperationException();
     }

     public static void setTask(AsyncTask task) {
         TaskLoader.task = task;
     }

    public static void cancel() {
         TaskLoader.task.cancel(true);
     }
}

Task doInBackground():

protected Void doInBackground(Params... params) {
    TaskLoader.setTask(this);
    ....
}

Aktivität onStop() oder onPause():

protected void onStop() {
    super.onStop();
    TaskLoader.cancel();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    final AddTask task = mAddTask;
    if (task != null && task.getStatus() != UserTask.Status.FINISHED) {
        final String bookId = task.getBookId();
        task.cancel(true);

        if (bookId != null) {
            outState.putBoolean(STATE_ADD_IN_PROGRESS, true);
            outState.putString(STATE_ADD_BOOK, bookId);
        }

        mAddTask = null;
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
    if (savedInstanceState.getBoolean(STATE_ADD_IN_PROGRESS)) {
        final String id = savedInstanceState.getString(STATE_ADD_BOOK);
        if (!BooksManager.bookExists(getContentResolver(), id)) {
            mAddTask = (AddTask) new AddTask().execute(id);
        }
    }
}

Sie können auch hinzufügen Android: configChanges = "keyboardHidden | Orientierung | Bildschirmgröße"

Ihr manifest Beispiel ich es hoffe Hilfe

 <application
    android:name=".AppController"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:configChanges="keyboardHidden|orientation|screenSize"
    android:theme="@style/AppTheme">
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top