Domanda

Ho letto molto su come salvare il mio stato istanza o come affrontare la mia attività ottenendo distrutta durante la rotazione dello schermo.

Sembra che ci siano un sacco di possibilità, ma non ho capito opere che uno migliori per il recupero di risultati di un AsyncTask.

Ho alcuni AsyncTasks che sono semplicemente iniziato di nuovo e chiamare il metodo isFinishing() dell'attività e se l'attività sta finendo si suole aggiornano nulla.

Il problema è che ho un compito che fa una richiesta a un servizio web che può fallire o avere successo e riavviare l'operazione si tradurrebbe in una perdita finanziaria per l'utente.

Come si risolvere questo problema? Quali sono i vantaggi o svantaggi delle possibili soluzioni?

È stato utile?

Soluzione

Il mio primo suggerimento sarebbe quello di fare in modo che effettivamente bisogno la vostra attività per essere ripristinato su una rotazione dello schermo (il comportamento predefinito). Ogni volta che ho avuto problemi con la rotazione ho aggiunto questo attributo al mio tag <activity> nel AndroidManifest.xml, e stato bene.

android:configChanges="keyboardHidden|orientation"

Sembra strano, ma quello che lo fa fuori mano per il metodo di onConfigurationChanged(), se non si fornisce uno solo non fa altro che ri-misurare il layout, che sembra essere un modo perfettamente adeguata di gestire la rotazione la maggior parte del tempo.

Altri suggerimenti

È possibile controllare come gestire AsyncTasks e cambiamenti di orientamento a code.google.com/p/shelves . Ci sono vari modi per farlo, quello che ho scelto in questa applicazione è quello di annullare qualsiasi operazione attualmente in esecuzione, salvare il suo stato e iniziare uno nuovo con lo stato salvato quando viene creato il nuovo Activity. E 'facile da fare, funziona bene e come bonus si prende cura di fermare le attività quando le foglie degli utenti della app.

È inoltre possibile utilizzare onRetainNonConfigurationInstance() di passare il vostro AsyncTask alla nuova Activity (essere attenti a che non perda il Activity precedente in questo modo però.)

Questa è la domanda più interessante che ho visto in merito ad Android !!! In realtà ho già alla ricerca di una soluzione nel corso degli ultimi mesi. Ancora non hanno risolto.

Attenzione, semplicemente ignorando il

android:configChanges="keyboardHidden|orientation"

roba non è sufficiente.

Si consideri il caso quando l'utente riceve una telefonata in tutta AsyncTask è in esecuzione. La richiesta è già in fase di elaborazione da parte del server, in modo che l'AsyncTask è in attesa per la risposta. In questo momento la vostra applicazione va in background, in quanto l'applicazione Telefono è appena venuto in primo piano. OS può uccidere il vostro attività dal momento che è in background.

Perché non tenere sempre un riferimento al AsyncTask corrente sul Singleton fornita da Android?

Ogni volta che un compito inizia, su PreExecute o sul costruttore, si definisce:

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

Ogni volta che finisce lo si imposta a NULL.

In questo modo si ha sempre un punto di riferimento, che ti permette di fare qualcosa di simile, o onCreate onResume come appropriato per la vostra logica specifica:

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

Se si tratta di nulla si sa che attualmente non c'è nessuno in esecuzione!

: -)

Il modo più corretto di questo è di usare un frammento di mantenere l'istanza dell'attività asincrono, oltre rotazioni.

Questo è il link per esempio molto semplice che la rende facile da seguire integrare questa tecnica nelle vostre applicazioni.

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

In Pro android 4. autore ha suggerire un modo piacevole, che si dovrebbe usare weak reference.

nota di riferimento Debole

Per il mio punto di vista, è meglio negozio AsyncTask via onRetainNonConfigurationInstance disaccoppiamento dall'oggetto Activity attuale e legandolo a un nuovo oggetto di attività dopo il cambiamento di orientamento. qui ho trovato un esempio molto bello come lavorare con AsyncTask e ProgressDialog.

Android: l'elaborazione in background / Async Opeartion con cambio di configurazione

Per mantenere gli stati di asincrona Opeartion durante il processo di fondo: si può prendere un aiuto di frammenti.

Vedere le seguenti operazioni:

Passaggio 1:. Creare un frammento senza intestazione diciamo task in background e aggiungere una classe compito async privata con in esso

Fase 2 (passo opzionale): se volete mettere un cursore di carico sulla parte superiore per l'utilizzo dell'attività sottostante Codice:

Passo 3: Nella tua attività principale implementare BackgroundTaskCallbacks interfaccia definita al punto 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
}

}

Una cosa da considerare è se il risultato della AsyncTask dovrebbe essere disponibile solo per l'attività che ha iniziato l'attività. Se sì, allora di Romain Guy risposta è la cosa migliore. Se dovesse essere a disposizione di altre attività della vostra applicazione, poi in onPostExecute è possibile utilizzare LocalBroadcastManager.

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

È inoltre necessario assicurarsi che l'attività gestisce correttamente situazione in cui la trasmissione viene inviato mentre l'attività è in pausa.

Date un'occhiata a questo posta . Questo implica un'AsyncTask eseguire operazione lunga e perdita di memoria in funzione quando la rotazione dello schermo avviene sia in un'applicazione di esempio. L'applicazione di esempio sono disponibili sul sourceforge

La mia soluzione.

Nel mio caso ho una catena di AsyncTasks con lo stesso contesto. Attività aveva un accesso solo a prima. Per annullare qualsiasi operazione in esecuzione ho fatto il seguente:

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);
    ....
}

onStop() Attività o 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);
        }
    }
}

è anche possibile aggiungere Android: configChanges = "keyboardHidden | orientamento | Screensize"

per il vostro esempio manifesta spero che l'aiuto

 <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">
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top