Zadanie w tle, okno postępu, zmiana orientacji-czy jest jakieś 100% działające rozwiązanie?

Pobieram niektóre dane z Internetu w wątku w tle (używam AsyncTask) i wyświetlam okno postępu podczas pobierania. Zmienia się orientacja, aktywność jest restartowana, a następnie moja AsyncTask jest zakończona - chcę zamknąć okno progess i rozpocząć nową aktywność. Ale wywołanie recorddialog czasami powoduje wyjątek(prawdopodobnie dlatego, że aktywność została zniszczona, a nowa aktywność nie została jeszcze uruchomiona).

Jaki jest najlepszy sposób radzenia sobie z tego typu problemem (aktualizacja interfejsu użytkownika z wątek tła, który działa nawet jeśli użytkownik zmienia orientację)? Czy ktoś z Google podał jakieś "oficjalne rozwiązanie"?

Author: grebulon, 2010-09-29

8 answers

Krok #1: uczyń swoją AsyncTask klasą zagnieżdżoną, lub całkowicie oddzielną klasą, po prostu nie wewnętrzną (niestatyczną) klasą zagnieżdżoną.

Krok # 2: przytrzymaj AsyncTask Activity przez element danych, ustawiony przez konstruktor i setter.

Krok # 3: podczas tworzenia AsyncTask, dostarcz bieżący Activity do konstruktora.

Krok # 4: w onRetainNonConfigurationInstance(), zwróć AsyncTask, po odłączeniu go od oryginalnej, teraz odchodzącej aktywności.

Krok # 5: W onCreate(), jeśli getLastNonConfigurationInstance() nie jest null, wrzuć go do klasy AsyncTask i zadzwoń do setera, aby powiązał twoją nową aktywność z zadaniem.

Krok # 6: nie odwołaj się do członka danych aktywności z doInBackground().

Jeśli zastosujesz się do powyższego przepisu, to wszystko zadziała. onProgressUpdate() i onPostExecute() są zawieszone między początkiem onRetainNonConfigurationInstance() a końcem następnego onCreate().

Oto przykładowy projekt demonstrujący technikę.

Innym podejściem jest porzucenie AsyncTask i przeniesienie swojej pracy do IntentService. Jest to szczególnie przydatne, jeśli praca do wykonania może być długa i powinna trwać niezależnie od tego, co użytkownik robi w zakresie działań (np. pobieranie dużego pliku). Możesz użyć uporządkowanej transmisji Intent, aby albo mieć aktywność reagującą na wykonywaną pracę (jeśli nadal znajduje się na pierwszym planie), albo podnieść Notification, aby poinformować użytkownika, czy praca została wykonana. Tutaj jest wpis na blogu z więcej na temat tego wzoru.

 335
Author: CommonsWare,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-15 02:02:03

Przyjęta odpowiedź była bardzo pomocna, ale nie ma okna dialogowego postępu.

Na szczęście dla ciebie, czytelniku, stworzyłem niezwykle wszechstronny i działający przykład AsyncTask z dialogiem postępu !

  1. rotacja działa, a okno dialogowe trwa.
  2. możesz anulować zadanie i okno dialogowe, naciskając przycisk Wstecz (jeśli chcesz tego zachowania).
  3. używa fragmentów.
  4. układ fragmentu pod działaniem zmienia się prawidłowo, gdy urządzenie obraca się.
 13
Author: Timmmm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-17 07:17:03

Pracowałem przez tydzień, aby znaleźć rozwiązanie tego dylematu bez uciekania się do edycji pliku manifestu. Założenia dla tego rozwiązania są następujące:

  1. zawsze musisz użyć okna dialogowego postępu
  2. tylko jedno zadanie jest wykonywane na raz
  3. zadanie musi być utrzymywane, gdy telefon jest obrócony, a okno dialogowe postępu ma być automatycznie odrzucane.

Realizacja

Będziesz musiał skopiować dwa pliki znalezione na na dole tego postu do Twojej przestrzeni roboczej. Upewnij się tylko, że:

  1. Wszystkie twoje Activity powinny się rozciągać BaseActivity

  2. W onCreate(), super.onCreate() powinien być wywołany po zainicjowaniu wszystkich członków, które muszą być dostępne przez ASyncTask s. również, zastąpić getContentViewId(), aby podać identyfikator układu formularza.

  3. Override onCreateDialog() jak zwykle , aby utworzyć okna dialogowe zarządzane przez działanie.

  4. Zobacz poniższy kod dla przykładowej statycznej klasy wewnętrznej, aby Twoja Asynchroniczne zadania. Możesz zapisać swój wynik w mResult, aby uzyskać dostęp później.


final static class MyTask extends SuperAsyncTask<Void, Void, Void> {

    public OpenDatabaseTask(BaseActivity activity) {
        super(activity, MY_DIALOG_ID); // change your dialog ID here...
                                       // and your dialog will be managed automatically!
    }

    @Override
    protected Void doInBackground(Void... params) {

        // your task code

        return null;
    }

    @Override
    public boolean onAfterExecute() {
        // your after execute code
    }
}

I na koniec, aby uruchomić nowe zadanie:

mCurrentTask = new MyTask(this);
((MyTask) mCurrentTask).execute();

To jest to! mam nadzieję, że to solidne rozwiązanie komuś pomoże.

BaseActivity.java (organizuj import samodzielnie)

protected abstract int getContentViewId();

public abstract class BaseActivity extends Activity {
    protected SuperAsyncTask<?, ?, ?> mCurrentTask;
    public HashMap<Integer, Boolean> mDialogMap = new HashMap<Integer, Boolean>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(getContentViewId());

        mCurrentTask = (SuperAsyncTask<?, ?, ?>) getLastNonConfigurationInstance();
        if (mCurrentTask != null) {
            mCurrentTask.attach(this);
            if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
                && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
        mCurrentTask.postExecution();
            }
        }
    }

    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
    super.onPrepareDialog(id, dialog);

        mDialogMap.put(id, true);
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        if (mCurrentTask != null) {
            mCurrentTask.detach();

            if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
                && mDialogMap.get((Integer) mCurrentTask.dialogId)) {
                return mCurrentTask;
            }
        }

        return super.onRetainNonConfigurationInstance();
    }

    public void cleanupTask() {
        if (mCurrentTask != null) {
            mCurrentTask = null;
            System.gc();
        }
    }
}

SuperAsyncTask.java

public abstract class SuperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
    protected BaseActivity mActivity = null;
    protected Result mResult;
    public int dialogId = -1;

    protected abstract void onAfterExecute();

    public SuperAsyncTask(BaseActivity activity, int dialogId) {
        super();
        this.dialogId = dialogId;
        attach(activity);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mActivity.showDialog(dialogId); // go polymorphism!
    }    

    protected void onPostExecute(Result result) {
        super.onPostExecute(result);
        mResult = result;

        if (mActivity != null &&
                mActivity.mDialogMap.get((Integer) dialogId) != null
                && mActivity.mDialogMap.get((Integer) dialogId)) {
            postExecution();
        }
    };

    public void attach(BaseActivity activity) {
        this.mActivity = activity;
    }

    public void detach() {
        this.mActivity = null;
    }

    public synchronized boolean postExecution() {
        Boolean dialogExists = mActivity.mDialogMap.get((Integer) dialogId);
        if (dialogExists != null || dialogExists) {
            onAfterExecute();
            cleanUp();
    }

    public boolean cleanUp() {
        mActivity.removeDialog(dialogId);
        mActivity.mDialogMap.remove((Integer) dialogId);
        mActivity.cleanupTask();
        detach();
        return true;
    }
}
 8
Author: Oleg Vaskevich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-08 03:26:43

Czy ktoś z Google podał jakieś "oficjalne rozwiązanie"?

Tak.

Rozwiązanie jest bardziej propozycją architektury aplikacji, niż tylko jakimś kodem.

Zaproponowali 3 wzorce projektowe Pozwala to aplikacji na synchronizację z serwerem, niezależnie od stanu aplikacji (będzie działać nawet jeśli użytkownik zakończy aplikację, użytkownik zmieni ekran, aplikacja zostanie zakończona, co drugi możliwe stan, w którym operacja danych w tle może zostać przerwana, to ją obejmuje)

Propozycja została wyjaśniona w aplikacji REST klienta Androida podczas przemówienia Google I / O 2010 przez Virgila Dobjanschiego. Trwa 1 godzinę, ale jest niezwykle warta obejrzenia.

Jego podstawą jest abstrakcja operacji sieciowych do Service, która działa niezależnie od dowolnego Activity w aplikacji. Jeśli pracujesz z bazami danych, Użycie ContentResolver i Cursor daje gotowy do użycia wzorzec obserwatora , który jest wygodny do aktualizacji interfejsu użytkownika bez dodatkowej logiki, po zaktualizowaniu lokalnej bazy danych o pobrane zdalne dane. Każdy inny kod po operacji będzie uruchamiany przez wywołanie zwrotne przekazane do Service (używam do tego podklasy ResultReceiver).

W każdym razie, moje wyjaśnienie jest dość niejasne, powinieneś zdecydowanie obejrzeć przemówienie.

 4
Author: Christopher Francisco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-08 16:49:31

Podczas gdy Mark ' S (CommonsWare) answer rzeczywiście działa dla zmiany orientacji, nie powiedzie się, jeśli aktywność zostanie zniszczona bezpośrednio (jak w przypadku rozmowy telefonicznej).

Możesz obsłużyć zmiany orientacji i rzadkie zniszczone zdarzenia aktywności, używając obiektu aplikacji do odwołania się do Asynktasku.

Jest doskonałe wyjaśnienie problemu i rozwiązania tutaj:

Uznanie należy się Ryanowi za to, że to rozgryzł.

 2
Author: Scott Biggs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-01 09:15:04

Po 4 latach Google rozwiązało problem wywołując setRetainInstance (true) w Activity onCreate. Zachowa wystąpienie aktywności podczas obracania urządzenia. Mam również proste rozwiązanie dla starszego Androida.

 1
Author: Singagirl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-26 01:54:43

Wszystkie akcje activity należy wywoływać za pomocą funkcji obsługi activity. Więc jeśli jesteś w jakimś wątku, powinieneś utworzyć Runnable i opublikować za pomocą funkcji obsługi Activitie. W przeciwnym razie Twoja aplikacja zawiesi się czasami z fatalnym wyjątkiem.

 0
Author: xpepermint,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-01 00:14:08

To jest moje rozwiązanie: https://github.com/Gotchamoh/Android-AsyncTask-ProgressDialog

Zasadniczo kroki są:

  1. używam onSaveInstanceState aby zapisać zadanie, jeśli jest jeszcze przetwarzam.
  2. W onCreate dostaję zadanie, jeśli zostało zapisane.
  3. W onPause odrzucam ProgressDialog, jeśli jest pokazany.
  4. W onResume pokazuję ProgressDialog Jeśli zadanie jest jeszcze przetwarzam.
 0
Author: Gotcha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-05 22:22:05