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"?
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.
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 !
- rotacja działa, a okno dialogowe trwa.
- możesz anulować zadanie i okno dialogowe, naciskając przycisk Wstecz (jeśli chcesz tego zachowania).
- używa fragmentów.
- układ fragmentu pod działaniem zmienia się prawidłowo, gdy urządzenie obraca się.
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:
- zawsze musisz użyć okna dialogowego postępu
- tylko jedno zadanie jest wykonywane na raz
- 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:
Wszystkie twoje
Activity
powinny się rozciągaćBaseActivity
W
onCreate()
,super.onCreate()
powinien być wywołany po zainicjowaniu wszystkich członków, które muszą być dostępne przezASyncTask
s. również, zastąpićgetContentViewId()
, aby podać identyfikator układu formularza.Override
onCreateDialog()
jak zwykle , aby utworzyć okna dialogowe zarządzane przez działanie.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;
}
}
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.
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ł.
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.
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.
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ą:
- używam
onSaveInstanceState
aby zapisać zadanie, jeśli jest jeszcze przetwarzam. - W
onCreate
dostaję zadanie, jeśli zostało zapisane. - W
onPause
odrzucamProgressDialog
, jeśli jest pokazany. - W
onResume
pokazujęProgressDialog
Jeśli zadanie jest jeszcze przetwarzam.
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