Saya banyak membaca tentang cara menyimpan status instance saya atau cara menangani aktivitas saya yang dimusnahkan selama rotasi layar.
Tampaknya ada banyak kemungkinan, tetapi saya belum menemukan kemungkinan mana yang paling cocok untuk mengambil hasil dari AsyncTask.
Saya memiliki beberapa AsyncTasks yang hanya dimulai lagi dan memanggil isFinishing()
metode aktivitas dan jika aktivitas selesai, mereka tidak akan memperbarui apa pun.
Masalahnya adalah saya memiliki satu Tugas yang melakukan permintaan ke layanan web yang dapat gagal atau berhasil dan memulai ulang tugas tersebut akan mengakibatkan kerugian finansial bagi pengguna.
Bagaimana Anda mengatasi ini? Apa keuntungan atau kerugian dari solusi yang mungkin?
setRetainInstance(true)
sebenarnya berguna ini.Jawaban:
Saran pertama saya adalah memastikan Anda benar-benar membutuhkan aktivitas Anda untuk disetel ulang pada rotasi layar (perilaku default). Setiap kali saya mengalami masalah dengan rotasi, saya telah menambahkan atribut ini ke
<activity>
tag saya di AndroidManifest.xml, dan baik-baik saja.android:configChanges="keyboardHidden|orientation"
Ini terlihat aneh, tetapi apa yang dilakukannya diserahkan ke
onConfigurationChanged()
metode Anda , jika Anda tidak menyediakannya, itu hanya tidak melakukan apa-apa selain mengukur ulang tata letak, yang tampaknya merupakan cara yang sangat memadai untuk menangani rotasi sebagian besar waktu .sumber
Anda dapat memeriksa bagaimana saya menangani
AsyncTask
perubahan orientasi dan di code.google.com/p/shelves . Ada berbagai cara untuk melakukannya, yang saya pilih di aplikasi ini adalah membatalkan tugas yang sedang berjalan, menyimpan statusnya, dan memulai yang baru dengan status tersimpan saat yang baruActivity
dibuat. Ini mudah dilakukan, berfungsi dengan baik dan sebagai bonus, ia menangani penghentian tugas Anda saat pengguna meninggalkan aplikasi.Anda juga dapat menggunakan
onRetainNonConfigurationInstance()
untuk meneruskanAsyncTask
ke yang baruActivity
(hati-hati jangan sampai bocor sebelumnyaActivity
dengan cara ini.)sumber
Ini adalah pertanyaan paling menarik yang pernah saya lihat tentang Android !!! Sebenarnya saya sudah mencari solusinya selama beberapa bulan terakhir. Masih belum terpecahkan.
Hati-hati, cukup menimpa
android:configChanges="keyboardHidden|orientation"
barang tidak cukup.
Pertimbangkan kasus ketika pengguna menerima panggilan telepon saat AsyncTask Anda sedang berjalan. Permintaan Anda sedang diproses oleh server, jadi AsyncTask menunggu tanggapan. Saat ini aplikasi Anda berjalan di latar belakang, karena aplikasi Telepon baru saja muncul di latar depan. OS dapat menghentikan aktivitas Anda karena berada di latar belakang.
sumber
Mengapa Anda tidak selalu menyimpan referensi ke AsyncTask saat ini di Singleton yang disediakan oleh Android?
Kapan pun tugas dimulai, di PreExecute atau di builder, Anda menentukan:
((Application) getApplication()).setCurrentTask(asyncTask);
Setiap kali selesai Anda mengaturnya ke nol.
Dengan begitu, Anda selalu memiliki referensi yang memungkinkan Anda melakukan sesuatu seperti, onCreate atau onResume yang sesuai dengan logika spesifik Anda:
this.asyncTaskReference = ((Application) getApplication()).getCurrentTask();
Jika nol, Anda tahu bahwa saat ini tidak ada yang berjalan!
:-)
sumber
Application
Instance memiliki siklus hidupnya sendiri - ia juga dapat dimatikan oleh OS, jadi solusi ini dapat menyebabkan bug yang sulit direproduksi.Cara paling tepat untuk ini adalah dengan menggunakan fragmen untuk mempertahankan instance tugas asinkron, melalui rotasi.
Berikut ini tautan ke contoh yang sangat sederhana yang memudahkan untuk mengikuti, mengintegrasikan teknik ini ke dalam aplikasi Anda.
https://gist.github.com/daichan4649/2480065
sumber
Masuk
Pro android 4
. Penulis telah menyarankan cara yang bagus, yang harus Anda gunakanweak reference
.Catatan referensi yang lemah
sumber
Menurut pandangan saya, lebih baik menyimpan asynctask melalui
onRetainNonConfigurationInstance
decoupling dari objek Activity saat ini dan mengikatnya ke objek Activity baru setelah orientasi berubah. Di sini saya menemukan contoh yang sangat bagus bagaimana bekerja dengan AsyncTask dan ProgressDialog.sumber
Android: pemrosesan latar belakang / Async Opeartion dengan perubahan konfigurasi
Untuk mempertahankan status operasi asinkron selama proses latar belakang: Anda dapat menggunakan bantuan fragmen.
Lihat langkah-langkah berikut ini:
Langkah 1: Buat fragmen tanpa header, katakanlah tugas latar belakang dan tambahkan kelas tugas asinkron pribadi dengan di dalamnya.
Langkah 2 (Langkah Opsional): jika Anda ingin meletakkan kursor pemuatan di atas aktivitas Anda gunakan kode di bawah ini:
Langkah 3: Dalam aktivitas utama Anda, implementasikan antarmuka BackgroundTaskCallbacks yang ditentukan pada langkah 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 }
}
sumber
Satu hal yang perlu dipertimbangkan adalah apakah hasil AsyncTask harus tersedia hanya untuk aktivitas yang memulai tugas. Jika ya, maka jawaban Romain Guy adalah yang terbaik. Jika itu harus tersedia untuk aktivitas lain dari aplikasi Anda, maka
onPostExecute
Anda dapat menggunakanLocalBroadcastManager
.LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("finished"));
Anda juga perlu memastikan bahwa aktivitas menangani situasi dengan benar saat siaran dikirim saat aktivitas dihentikan sementara.
sumber
Lihat posting ini . Postingan ini melibatkan AsyncTask yang menjalankan operasi yang berjalan lama dan kebocoran memori saat rotasi layar terjadi dalam satu aplikasi sampel. Aplikasi sampel tersedia di pemalsuan sumber
sumber
Solusi saya.
Dalam kasus saya, saya punya rantai AsyncTasks dengan konteks yang sama. Aktivitas hanya memiliki akses ke yang pertama. Untuk membatalkan tugas yang sedang berjalan, saya melakukan hal berikut:
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); } }
Tugas
doInBackground()
:protected Void doInBackground(Params... params) { TaskLoader.setTask(this); .... }
Aktivitas
onStop()
atauonPause()
:protected void onStop() { super.onStop(); TaskLoader.cancel(); }
sumber
@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); } } }
sumber
Anda juga dapat menambahkan android: configChanges = "keyboardHidden | orientasi | screenSize"
untuk contoh nyata Anda, saya harap ini membantu
<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">
sumber