Android SDK AsyncTask doInBackground tidak berjalan (subclass)

90

Per 15/2/2012 saya belum menemukan penjelasan yang baik atau alasan mengapa ini tidak berhasil. Solusi terdekat adalah dengan menggunakan pendekatan Thread tradisional , tetapi mengapa menyertakan kelas yang tidak (tampaknya) berfungsi di Android SDK?

Evenin 'SO!

Saya memiliki subkelas AsyncTask:

// ParseListener had a callback which was called when an item was parsed in a
// RSS-xml, but as stated further down it is not used at all right now.
private class xmlAsync extends AsyncTask<String, RSSItem, Void> implements ParseListener

Itu dijalankan seperti ini:

xmlAsync xmlThread = new xmlAsync();

xmlThread.execute("http://www.nothing.com");

Sekarang subclass ini mengalami sedikit kesalahan. Sebelumnya itu melakukan beberapa xml-parsing, tetapi ketika saya perhatikan bahwa doInBackground () tidak dipanggil, saya melepasnya , baris demi baris, akhirnya berakhir hanya dengan ini:

@Override
protected Void doInBackground(String... params) 
{
    Log.v(TAG, "doInBackground");
        return null;
}

Yang, untuk beberapa alasan, tidak mencatat apa pun. Namun, saya menambahkan ini:

@Override
protected void onPreExecute() 
{
        Log.v(TAG, "onPreExecute");
        super.onPreExecute();
}

Dan baris itu memang dicatat saat menjalankan utas. Jadi entah bagaimana onPreExecute () dipanggil tetapi tidak doInBackground () . Saya memiliki AsyncTask lain yang berjalan di latar belakang pada saat yang sama yang berfungsi dengan baik.

Saat ini saya menjalankan aplikasi di emulator, SDK Versi 15, Eclipse, Mac OS X 10.7.2, dekat Kutub Utara.

EDIT:

@Override
    protected void onProgressUpdate(RSSItem... values) {

        if(values[0] == null)
        {
                            // activity function which merely creates a dialog
            showInputError();
        }
        else
        {

            Log.v(TAG, "adding "+values[0].toString());
            _tableManager.addRSSItem(values[0]);
        }


        super.onProgressUpdate(values);
    }

_tableManager.addRSSItem () sedikit banyak menambahkan baris ke SQLiteDatabase, diinisialisasi dengan konteks aktivitas. publishProgress () dipanggil oleh callback Interface ParseListener. Namun, karena saya bahkan tidak melakukan apa pun kecuali log.v di doInBackground () saya pertama kali menemukan ini bahkan tidak perlu untuk dimunculkan.

EDIT 2:

Baiklah, untuk memperjelas, ini adalah AsyncTask lainnya, yang dijalankan dalam aktivitas yang sama dan berfungsi dengan baik.

private class dbAsync extends AsyncTask<Void, RSSItem, Void>
{
    Integer prevCount;
    boolean run;

    @Override
    protected void onPreExecute() {
        run = true;
        super.onPreExecute();
    }

    @Override
    protected Void doInBackground(Void... params) {
        // TODO Auto-generated method stub
        run = true;
        prevCount = 0;

        while(run)
        {
            ArrayList<RSSItem> items = _tableManager.getAllItems();

            if(items != null)
            {
                if(items.size() > prevCount)
                {
                    Log.v("db Thread", "Found new item(s)!");
                    prevCount = items.size();

                    RSSItem[] itemsArray = new RSSItem[items.size()];

                    publishProgress(items.toArray(itemsArray));
                }
            }               

            SystemClock.sleep(5000);
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(RSSItem... values) {

        ArrayList<RSSItem> list = new ArrayList<RSSItem>();

        for(int i = 0; i < values.length; i++)
        {
            list.add(i, values[i]);
        }

        setItemsAndUpdateList(list);

        super.onProgressUpdate(values);
    }

    @Override
    protected void onCancelled() {
        run = false;

        super.onCancelled();
    }
}

EDIT 3:

Sigh, maaf saya buruk dalam mengajukan pertanyaan. Tapi di sini adalah inisialisasi Tugas.

xmlAsync _xmlParseThread;
dbAsync _dbLookup;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

_dbLookup = new dbAsync();
_dbLookup.execute();

_xmlParseThread = new xmlAsync();       
_xmlParseThread.execute("http://www.nothing.com", null);
}
SeruK
sumber
apakah mungkin aktivitas tersebut selesai sebelum tugas selesai?
Paul Nikonowicz
Sangat tidak mirip. Dalam hal ini utas saya yang lain tidak akan berjalan, bukan? Dan saya hanya punya satu aktivitas sekarang.
SeruK
2
Aplikasi saya memiliki masalah yang sama - doInBackground tidak dipanggil atau dipanggil dengan penundaan yang sangat lama. Berikut adalah pengamatan saya yang terbatas: kode yang persis sama berfungsi dengan sempurna pada smartphone Android 2.3.3 dan dan emulator Android 2.3.3, tetapi masalah ini terjadi pada tablet Android 4.0.3 dan banyak emulator Android 4.xx. Sangat menggoda untuk menyimpulkan bahwa masalah ini diperkenalkan di versi Android yang lebih baru.
Hong
Maaf, tapi saya lupa menyebutkan masalah ini hanya terjadi dengan AsyncTask kedua dari sebuah Aktivitas AsyncTask pertama selalu berfungsi dengan baik.
Hong
Hong, apakah Anda sudah mencoba jawaban Matthieu? Saya sebagian besar keluar dari ATM game Android dan belum bekerja dengannya untuk sementara waktu, jadi saya tidak tahu apakah jawabannya benar-benar berfungsi. Jika bukan karena Anda, maka mungkin buruk bagi saya untuk menerima jawabannya ...
SeruK

Jawaban:

107

Solusi Matthieu akan bekerja dengan baik untuk sebagian besar orang, tetapi beberapa dapat menghadapi masalah; kecuali menggali banyak tautan yang disediakan di sini atau dari web, seperti penjelasan Anders Göransson . Saya mencoba meringkas beberapa bacaan lain di sini dan dengan cepat menjelaskan solusi jika executeOnExecutor masih berfungsi dalam satu utas ...

Perilaku AsyncTask().execute();telah berubah melalui versi Android. Sebelum tugas Donut (Android: 1.6 API: 4) dijalankan secara serial, dari Donut hingga Gingerbread (Android: 2.3 API: 9) tugas dijalankan secara paralel; sejak Honeycomb (Android: 3.0 API: 11) eksekusi dialihkan kembali ke sekuensial; AsyncTask().executeOnExecutor(Executor)namun metode baru ditambahkan untuk eksekusi paralel.

Dalam pemrosesan berurutan, semua tugas Async dijalankan dalam satu utas dan karenanya harus menunggu sebelum tugas sebelumnya berakhir. Jika Anda perlu segera menjalankan kode, Anda memerlukan tugas untuk diproses secara paralel di utas terpisah.

Dengan AsyncTask, eksekusi serial tidak tersedia antara versi Donut dan Honeycomb, sementara eksekusi paralel tidak tersedia sebelum Donut.

Untuk pemrosesan paralel setelah Donut: Periksa versi Build dan berdasarkan metode use .execute () atau .executeOnExecutor () itu. Kode berikut dapat membantu ...

AsyncTask<Void,Void,Void> myTask = new AsyncTask<Void,Void,Void>() { ... }; // ... your AsyncTask code goes here
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
    myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
    myTask.execute();

NOTE:Fungsi .executeOnExecutor()memiliki pemeriksaan apakah targetSdkVersionproyek kurang dari atau sama dengan HONEYCOMB_MR1(Android: 2.1 API: 7) kemudian memaksa eksekutor untuk menjadi THREAD_POOL_EXECUTOR(yang menjalankan Tugas secara berurutan di pos Honeycomb).
Jika Anda belum menentukan targetSdkVersionmaka minSdkVersionsecara otomatis dianggap sebagai targetSdkVersion.
Karenanya untuk menjalankan AsyncTask Anda secara paralel pada posting Honeycomb, Anda tidak dapat membiarkannya targetSdkVersionkosong.

Nashe
sumber
1
Jawaban yang sangat bagus. Meskipun Matthieu tidak salah, saya menerima ini, karena Anda menambahkan banyak info penting.
SeruK
@Nashe Terima kasih banyak. Ini sangat membantu. Saya berjuang dengan masalah yang sama selama 3 hari. Terima kasih lagi :)
Menyelamatkan hariku! Semoga jawaban ini lebih mudah ditemukan.
zjk
4
Hai @Nashe, Masalah saya agak canggung. Sebelum hari ini saya menggunakan metode .execute () di AsyncTask dan kodenya bekerja dengan sempurna. Tapi hari ini saya mendapat masalah - kontrol tidak masuk ke metode doInBackground (). Meskipun solusi yang Anda berikan berhasil, saya bingung bagaimana cara kerjanya sebelumnya tanpa solusi tersebut. Saya menggunakan perangkat yang sama sebelumnya dan sekarang.
Ravi Sisodia
Kenapa harus begini? Mengapa tidak bekerja sesuai ekspektasi? :(
Nikolay R
160

Anda harus memeriksa jawaban ini: https://stackoverflow.com/a/10406894/347565 dan tautan ke grup google yang disertakan.

Saya memiliki masalah yang sama dengan Anda, masih tidak jelas mengapa itu tidak berfungsi, tetapi saya mengubah kode saya seperti ini dan masalahnya hilang:

ASyncTask<Void,Void,Void> my_task = new ASyncTask<Void,Void,Void>() { ... };
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
    my_task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
else
    my_task.execute((Void[])null);
Matthieu
sumber
Saya sangat buruk dalam melihat kembali posting ini; Saya sudah lama pindah dari proyek ini. Saya hanya akan menerima ini sebagai jawabannya karena orang-orang tampaknya mengatakan itu berhasil.
SeruK
ini berhasil untuk saya sekarang tetapi saya berharap saya mengerti mengapa. itu bekerja dengan baik dengan eksekusi sebelum kemudian berhenti. satu hal yang saya lakukan adalah memulai asynctask baru di dalam onPostExecute dari asynctask yang sama (yaitu, saya menyebutnya secara rekursif) mungkin itu terkait dengan masalah?
steveh
Saya pikir ini akan memberi Anda semua penjelasan yang Anda butuhkan: commonsware.com/blog/2012/04/20/…
Matthieu
1
@ Matthieu: Pak, saya benar-benar tidak bisa cukup berterima kasih !!! Saya telah memikirkan masalah ini selama berjam-jam dan solusi Anda membuat semuanya bekerja seperti pesona! Terima kasih banyak atas jawaban yang fantastis. Saya berharap saya dapat memberikan lebih dari satu suara positif !!
Swayam
9

Anda dapat melakukannya dengan dua cara:

Cara 1 :

if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) // Above Api Level 13
  {
      asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  }
else // Below Api Level 13
  {
      asyncTask.execute();
  }

Jika cara 1 tidak berhasil untuk Anda, coba cara 2 .

Cara 2 :

int mCorePoolSize = 60;
int mMaximumPoolSize = 80;
int mKeepAliveTime = 10;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(mMaximumPoolSize);
Executor mCustomThreadPoolExecutor = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, mKeepAliveTime, TimeUnit.SECONDS, workQueue);
asyncTask.executeOnExecutor(mCustomThreadPoolExecutor);

Semoga ini bisa membantu Anda.

Hiren Patel
sumber
super sobat itu berfungsi tetapi tugas Async berikutnya yang menggunakan AsyncTask.THREAD_POOL_EXECUTOR semakin gagal, jadi saya perlu mengubah dengan CustomExecutor di seluruh proyek saya kira :(
kumar
@vinu, saya sarankan Anda untuk menggunakan tugas async umum dan metode umum untuk menjalankan AsyncTask. Semoga ini bisa membantu Anda.
Hiren Patel
2
Hei, ini sangat membantuku! Terima kasih!
Justin Ebby
6

Saya memiliki masalah yang sama: tidak dapat mengeksekusi AsyncTask kedua setelah saya memanggil "mengeksekusi" pada yang pertama: doInBackground hanya dipanggil untuk yang pertama.

Untuk menjawab mengapa ini terjadi, periksa jawaban ini (perilaku berbeda tergantung pada SDK)

Namun, untuk kasus Anda, kendala ini dapat dihindari dengan menggunakan executeOnExecutor (tersedia mulai dari 3.0 yang berfungsi untuk saya menggunakan 4.0.3) tetapi waspadalah terhadap batasan ukuran dan antrian kumpulan Thread.

Bisakah Anda mencoba sesuatu seperti ini:

xmlAsync _xmlParseThread;
dbAsync _dbLookup;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

_dbLookup = new dbAsync();
_dbLookup.execute();

_xmlParseThread = new xmlAsync();       
_xmlParseThread.executeOnExecutor(_dbLookup.THREAD_POOL_EXECUTOR
 ,"http://www.nothing.com", null);
}

Untuk pertanyaan pembaruan Anda: dijelaskan di dokumen Pada dasarnya hanya untuk menghindari semua masalah yang mungkin datang dari multithreading seperti intereferensi ....

code7amza.dll
sumber
5

Satu hal yang ingin saya ketahui, dan itu mungkin benar-benar memperbaiki masalah Anda, adalah di mana Anda membuat instance kelas Anda dan memanggil metode execute ()? Jika Anda membaca dokumentasi untuk AsyncTask, kedua operasi tersebut harus berlangsung di thread UI utama. Jika Anda membuat objek dan memanggil eksekusi dari beberapa utas lain, maka onPreExecute mungkin aktif, saya tidak 100% yakin di sini, tetapi utas latar belakang tidak akan dibuat dan dijalankan.

Jika Anda membuat instance AsyncTask dari thread latar belakang, atau beberapa operasi lain yang tidak berlangsung pada thread UI utama, Anda dapat mempertimbangkan untuk menggunakan metode: Activity.runOnUiThread (Runnable)

Anda memerlukan akses ke instance Activity yang sedang berjalan untuk memanggil metode itu, tetapi itu akan memungkinkan Anda menjalankan kode pada UI thread dari beberapa kode lain yang tidak berjalan di UI thread.

Harapan itu masuk akal. Beri tahu saya jika saya dapat membantu lebih lanjut.

David

David C. Sainte-Claire
sumber
menambahkan jawaban Anda utas ini memiliki jawaban yang menarik stackoverflow.com/questions/4080808/… .
manjusg
Terima kasih atas jawaban yang bagus! Saya menaikkan ini karena menurut saya itu mungkin masalah umum pemula AsyncTask. Sayangnya, ini bukanlah jawaban yang tepat untuk masalah ini. Kedua kelas tersebut dibuat instance-nya dalam onCreate () aktivitas yang berjalan di thread UI utama. Saya hanya memiliki satu aktivitas dalam proyek ini.
SeruK
@manjusg Saya telah mempertimbangkan selama ini bahwa itu ada hubungannya dengan AsyncTask yang tidak stabil, mungkin lebih banyak ketika beberapa dijalankan secara bersamaan. Jika ya, mengapa?
SeruK
Saya tidak benar-benar tahu kebijakan apa yang dimiliki SO tentang memposting dengan cepat tiga kali berturut-turut, tetapi saya menemukan ini di utas lain ... foo.jasonhudgins.com/2010/05/limitations-of-asynctask.html "AsyncTask using antrean kerja internal statis dengan batas kode keras 10 elemen. " Ini mungkin membuktikan sesuatu, tapi saya hanya punya dua contoh AsyncTask-subclass! Saya benar-benar ingin menghindari penggunaan metode threading normal karena pada akhirnya akan ada banyak penguraian yang dilakukan di dere.
SeruK
2

Android itu Brutal! Saya tidak percaya ini, implementasi yang tidak stabil yang berubah dari hari ke hari. Suatu hari ini adalah satu utas, selanjutnya 5 utas lainnya adalah 128.

Ngomong-ngomong di sini ada hampir penurunan pengganti stok AsyncTask. Anda bahkan dapat menyebutnya AsyncTask jika Anda mau, tetapi untuk menghindari kebingungan, ini disebut ThreadedAsyncTask. Anda perlu memanggil executeStart () daripada menjalankan karena execute () sudah final.

/**
 * @author Kevin Kowalewski
 *
 */
public abstract class ThreadedAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { 
    public AsyncTask<Params, Progress, Result> executeStart(Params... params){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
            return executePostHoneycomb(params);
        }else{
            return super.execute(params);
        }
    }


    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private AsyncTask<Params, Progress, Result> executePostHoneycomb(Params... params){
        return super.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 
    }
}
Kevin Parker
sumber
Tunggu, Anda tidak mengatakan beberapa versi Android membatasi operasi asinkron ke satu utas, bukan? Itu akan sangat konyol. (Saya sudah keluar dari game Android untuk sementara waktu. :))
SeruK
Ya, pada Android 3.0+ jika Anda tidak menggunakan AsyncTask.THREAD_POOL_EXECUTOR Anda hanya mendapatkan satu kumpulan utas. Cobalah sendiri, dua AsyncTask dan cukup tidur di doInBackground satu. Dari dokumentasi Android AsyncTask: "Dimulai dengan HONEYCOMB, tugas dijalankan pada satu thread untuk menghindari error aplikasi umum yang disebabkan oleh eksekusi paralel."
Kevin Parker
1

Saya tahu ini mungkin sangat terlambat untuk utas, tetapi ada alasan mengapa itu tidak akan berfungsi pada emulator Android nanti. Ketika asynctask diperkenalkan android hanya membiarkan Anda menjalankan satu per satu, kemudian beberapa saat kemudian, saya tidak yakin versi yang mana, mereka memungkinkan Anda untuk menjalankan beberapa asynctask sekaligus, ini menyebabkan masalah di banyak aplikasi, dan di Honeycomb + mereka kembali ke hanya memungkinkan satu asynctask dijalankan pada satu waktu. Kecuali Anda mengubah kumpulan utas secara manual. Harapan yang menjelaskan satu atau dua hal bagi orang-orang.

Fre AkyTurtle-Records
sumber
0

Saya pikir itu SDK. saya memiliki masalah yang sama, dan setelah mengubah sdk target dari 15 menjadi 11, semuanya bekerja dengan sempurna.

dengan sdk15, meskipun AsyncTask.Status sedang RUNNING, doInBackground tidak pernah dipanggil. Saya pikir itu ada hubungannya dengan utas ui sekalipun.

phobus.dll
sumber
Saya tidak dapat menyangkal atau mengonfirmasi karena saya tidak benar-benar punya waktu sekarang untuk mengujinya. Yang bisa saya katakan adalah bahwa saya menggunakan SDK 15, jadi sangat mungkin.
SeruK
0

Berdasarkan jawaban Matthieu, di bawah kelas pembantu untuk mengeksekusi Anda AsyncTaskdengan benar bergantung pada versi SDK untuk menghindari duplikasi kode dalam aplikasi Anda:

import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.os.Build;

public class AsyncTaskExecutor<Params, Progress, Result> {

  @SuppressLint("NewApi")
  public AsyncTask<Params, Progress, Result> execute(final AsyncTask<Params, Progress, Result> asyncTask, final Params... params){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
      return asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    }  else{
      return asyncTask.execute(params);
    }
  }

}

Contoh penggunaan:

public class MyTask extends AsyncTask<Void, Void, List<String>> {

...

final MyTask myTask = new MyTask();
new AsyncTaskExecutor<Void, Void, List<String>>().execute(myTask);
LG
sumber