Android AsyncTask untuk operasi yang berjalan lama

91

Mengutip dokumentasi untuk AsyncTask yang ditemukan di sini , dikatakan:

AsyncTasks idealnya digunakan untuk operasi singkat (paling lama beberapa detik.) Jika Anda perlu menjaga agar utas tetap berjalan untuk jangka waktu yang lama, sangat disarankan Anda menggunakan berbagai API yang disediakan oleh pacakge java.util.concurrent seperti Pelaksana, ThreadPoolExecutor, dan FutureTask.

Sekarang pertanyaan saya muncul: mengapa? The doInBackground()fungsi berjalan off thread UI jadi apa salahnya dengan memiliki operasi lama berjalan di sini?

pengguna1730789
sumber
2
Masalah yang saya hadapi saat menerapkan aplikasi (yang menggunakan asyncTask) ke perangkat sebenarnya adalah bahwa doInBackgroundfungsi yang berjalan lama membekukan layar jika bilah kemajuan tidak digunakan.
venkatKA
2
Karena AsyncTask terkait dengan Aktivitas tempat ia diluncurkan. Jadi, jika Aktivitas dimatikan, instance AsyncTask Anda juga bisa dimatikan.
IgorGanapolsky
Bagaimana jika saya membuat AsyncTask dalam Layanan? Bukankah itu menyelesaikan masalah?
eddy
1
Gunakan IntentService, Solusi sempurna untuk menjalankan operasi lama di latar belakang.
Keyur Thumar

Jawaban:

120

Ini adalah pertanyaan yang sangat bagus, dibutuhkan waktu sebagai Pemrogram Android untuk sepenuhnya memahami masalahnya. Memang AsyncTask memiliki dua masalah utama yang berkaitan:

  • Mereka kurang terikat dengan siklus hidup aktivitas
  • Mereka membuat kebocoran memori dengan sangat mudah.

Di dalam aplikasi RoboSpice Motivations ( tersedia di Google Play ) kami menjawab pertanyaan itu secara rinci. Ini akan memberikan pandangan mendalam tentang AsyncTasks, Loader, fitur dan kekurangannya dan juga memperkenalkan Anda pada solusi alternatif untuk permintaan jaringan: RoboSpice. Permintaan jaringan adalah persyaratan umum di Android dan pada dasarnya operasi yang berjalan lama. Berikut adalah kutipan dari aplikasi:

AsyncTask dan siklus hidup Aktivitas

AsyncTasks tidak mengikuti siklus hidup instance Aktivitas. Jika Anda memulai AsyncTask di dalam sebuah Aktivitas dan Anda memutar perangkat, Aktivitas tersebut akan dimusnahkan dan sebuah instance baru akan dibuat. Tapi AsyncTask tidak akan mati. Itu akan terus hidup sampai selesai.

Dan setelah selesai, AsyncTask tidak akan memperbarui UI Aktivitas baru. Memang itu memperbarui contoh sebelumnya dari aktivitas yang tidak ditampilkan lagi. Hal ini dapat menyebabkan Pengecualian tipe java.lang.IllegalArgumentException: Tampilan tidak dilampirkan ke pengelola jendela jika Anda menggunakan, misalnya, findViewById untuk mengambil tampilan di dalam Aktivitas.

Masalah kebocoran memori

Sangat mudah untuk membuat AsyncTasks sebagai kelas dalam Aktivitas Anda. Karena AsyncTask perlu memanipulasi tampilan Aktivitas saat tugas selesai atau sedang berlangsung, penggunaan kelas dalam dari Aktivitas tampaknya nyaman: kelas dalam dapat mengakses langsung bidang apa pun dari kelas luar.

Namun demikian, itu berarti kelas dalam akan menyimpan referensi tak terlihat pada contoh kelas luarnya: Aktivitas.

Dalam jangka panjang, ini menghasilkan kebocoran memori: jika AsyncTask bertahan lama, itu membuat aktivitas "hidup" sedangkan Android ingin membuangnya karena tidak bisa lagi ditampilkan. Aktivitas tidak dapat dikumpulkan sampah dan itu adalah mekanisme utama Android untuk menjaga sumber daya di perangkat.


Ini benar-benar ide yang sangat buruk untuk menggunakan AsyncTasks untuk operasi yang berjalan lama. Namun demikian, mereka baik-baik saja untuk yang berumur pendek seperti memperbarui Tampilan setelah 1 atau 2 detik.

Saya mendorong Anda untuk mengunduh aplikasi RoboSpice Motivations , itu benar-benar menjelaskan hal ini secara mendalam dan memberikan contoh dan demonstrasi tentang berbagai cara untuk melakukan beberapa operasi latar belakang.

Snicolas
sumber
@Nicolas Hai. Saya memiliki aplikasi yang memindai data dari tag NFC dan mengirim ke server. Ini berfungsi dengan baik di area sinyal yang baik tetapi di mana tidak ada sinyal, AsyncTask yang membuat panggilan web tetap berjalan. misalnya kotak dialog kemajuan berjalan selama beberapa menit dan kemudian ketika tidak ada layar menjadi hitam dan tidak responsif. AsyncTask saya adalah kelas dalam. Saya sedang menulis Penangan untuk membatalkan tugas setelah X detik. Aplikasi ini sepertinya mengirimkan data lama ke server beberapa jam setelah pemindaian. Mungkinkah ini karena AsyncTask tidak selesai dan mungkin selesai beberapa jam setelahnya? Saya akan menghargai setiap wawasan. terima kasih
turtleboy
Lacak untuk melihat apa yang terjadi. Tapi ya, itu sangat mungkin! Jika Anda mendesain asynctask Anda dengan baik, Anda dapat membatalkannya dengan baik, itu akan menjadi titik awal yang baik jika Anda tidak ingin bermigrasi ke RS atau layanan ...
Snicolas
@Snicolas Terima kasih atas jawabannya. Saya membuat posting di SO kemarin menguraikan masalah saya dan menunjukkan kode penangan yang telah saya tulis untuk mencoba menghentikan AsyncTask setelah 8 detik. Maukah Anda melihatnya, jika Anda punya waktu? Akankah memanggil AsyncTask.cancel (true) dari Handler membatalkan tugas dengan benar? Saya tahu saya harus memeriksa secara berkala nilai iscancelled () di doInBackgroud saya, tetapi menurut saya tidak berlaku untuk keadaan saya karena saya hanya membuat panggilan web satu baris HttpPost dan tidak menerbitkan pembaruan di UI. Apakah ada perubahan pada AsyncTask misalnya apakah mungkin untuk membuat HttPost dari IntentService
turtleboy
Anda harus mencoba RoboSpice (di github). ;)
Snicolas
38

kenapa?

Karena AsyncTask, secara default, menggunakan kumpulan thread yang tidak Anda buat . Jangan pernah mengikat sumber daya dari pangkalan yang tidak Anda buat, karena Anda tidak tahu apa persyaratan pangkalan itu. Dan jangan pernah mengikat sumber daya dari kumpulan yang tidak Anda buat jika dokumentasi untuk kumpulan tersebut memberi tahu Anda untuk tidak melakukannya, seperti yang terjadi di sini.

Secara khusus, dimulai dengan Android 3.2, kumpulan utas yang digunakan secara AsyncTaskdefault (untuk aplikasi dengan android:targetSdkVersionset ke 13 atau lebih tinggi) hanya memiliki satu utas di dalamnya - jika Anda mengikat utas ini tanpa batas waktu, tidak ada tugas Anda yang lain yang akan berjalan.

CommonsWare
sumber
Terima kasih atas penjelasan ini .. Saya tidak dapat menemukan kesalahan apa pun tentang penggunaan AsyncTasks untuk operasi yang berjalan lama (meskipun saya biasanya mendelegasikannya ke Layanan sendiri), tetapi argumen Anda tentang mengikat ThreadPool itu (yang memang Anda dapat tidak membuat asumsi tentang ukurannya) tampaknya tepat. Sebagai pelengkap postingan Anup tentang penggunaan layanan: Layanan itu sendiri juga harus menjalankan tugas di thread latar belakang, dan tidak memblokir thread utamanya sendiri. Opsinya bisa berupa IntentService, atau, untuk persyaratan konkurensi yang lebih kompleks, terapkan strategi multithreading Anda sendiri.
berjemur
Apakah sama bahkan jika saya memulai AsyncTask di dalam Layanan ?? Maksud saya, apakah operasi jangka panjang masih menjadi masalah?
eddy
1
@eddy: Ya, karena itu tidak mengubah sifat kumpulan utas. Untuk a Service, gunakan a Threadatau a ThreadPoolExecutor.
CommonsWare
Terima kasih @CommonsWare hanya pertanyaan terakhir, apakah TimerTasks memiliki kelemahan yang sama dengan AsyncTasks? atau Apakah itu hal yang sama sekali berbeda?
eddy
1
@eddy: TimerTaskberasal dari Java standar, bukan Android. TimerTasksebagian besar telah ditinggalkan demi ScheduledExecutorService(yang, terlepas dari namanya, sebagai bagian dari Java standar). Tidak ada yang terikat dengan Android, jadi Anda masih memerlukan layanan jika mengharapkan hal-hal itu berjalan di latar belakang. Dan, Anda benar - benar harus mempertimbangkan AlarmManager, dari Android, jadi Anda tidak perlu layanan berkeliaran hanya menonton jam berdetak.
CommonsWare
4

Tugas Aysnc adalah utas khusus yang masih dimaksudkan untuk digunakan dengan GUI aplikasi Anda, tetapi tetap mempertahankan tugas utas UI yang banyak sumber daya. Jadi, ketika hal-hal seperti memperbarui daftar, mengubah tampilan, dll. Mengharuskan Anda melakukan beberapa operasi pengambilan atau memperbarui operasi, Anda harus menggunakan tugas asinkron sehingga Anda dapat menjauhkan operasi ini dari UI thread tetapi perhatikan bahwa operasi ini masih terhubung ke UI entah bagaimana caranya. .

Untuk tugas yang berjalan lebih lama, yang tidak memerlukan pembaruan UI, Anda bisa menggunakan layanan karena mereka bisa hidup bahkan tanpa UI.

Jadi untuk tugas-tugas singkat, gunakan tugas-tugas asinkron karena mereka bisa terbunuh oleh OS setelah aktivitas pemijahan Anda mati (biasanya tidak akan mati di tengah operasi tetapi akan menyelesaikan tugasnya). Dan untuk tugas yang panjang dan berulang, gunakan layanan sebagai gantinya.

untuk info lebih lanjut, Lihat utas:

AsyncTask lebih dari beberapa detik?

dan

AsyncTask tidak akan berhenti meskipun aktivitas telah dimusnahkan

Anup Cowkur
sumber
1

Masalah dengan AsyncTask adalah jika ia didefinisikan sebagai kelas dalam non-statis aktivitas, ia akan memiliki referensi ke aktivitas. Dalam skenario di mana aktivitas penampung tugas asinkron selesai, tetapi pekerjaan latar belakang di AsyncTask berlanjut, objek aktivitas tidak akan dikumpulkan sampah karena ada referensi ke sana, hal ini menyebabkan kebocoran memori.

Solusi untuk memperbaikinya adalah dengan mendefinisikan tugas asinkron sebagai kelas dalam statis aktivitas dan menggunakan referensi lemah ke konteks.

Namun tetap saja, sebaiknya gunakan untuk tugas latar belakang yang sederhana dan cepat. Untuk mengembangkan aplikasi dengan kode bersih, lebih baik menggunakan RxJava untuk menjalankan tugas latar belakang yang kompleks dan memperbarui UI dengan hasil darinya.

Arnav Rao
sumber