Android - Mengatur Timeout untuk AsyncTask?

125

Saya memiliki AsyncTaskkelas yang saya jalankan yang mengunduh daftar besar data dari situs web.

Jika pengguna akhir memiliki koneksi data yang sangat lambat atau tidak stabil pada saat penggunaan, saya ingin membuat AsyncTaskbatas waktu setelah jangka waktu tertentu. Pendekatan pertama saya untuk ini adalah seperti ini:

MyDownloader downloader = new MyDownloader();
downloader.execute();
Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
  @Override
  public void run() {
      if ( downloader.getStatus() == AsyncTask.Status.RUNNING )
          downloader.cancel(true);
  }
}, 30000 );

Setelah memulai AsyncTask, penangan baru dimulai yang akan membatalkan AsyncTasksetelah 30 detik jika masih berjalan.

Apakah ini pendekatan yang bagus? Atau adakah sesuatu yang dibangun di dalamnya AsyncTaskyang lebih cocok untuk tujuan ini?

Jake Wilson
sumber
18
Setelah mencoba beberapa pendekatan berbeda, saya menyimpulkan bahwa pertanyaan Anda seharusnya merupakan jawaban yang diterima.
JohnEye
Terima kasih atas pertanyaannya. Saya benar-benar mengalami masalah yang sama dan potongan kode Anda membantu saya +1
Ahmad Dwaik 'Warlock'
1
Pertanyaan Anda sendiri merupakan jawaban yang bagus +50 :)
karthik kolanji

Jawaban:

44

Ya, ada AsyncTask.get ()

myDownloader.get(30000, TimeUnit.MILLISECONDS);

Perhatikan bahwa dengan memanggil ini di thread utama (AKA. UI thread) akan memblokir eksekusi, Anda mungkin perlu memanggilnya di thread terpisah.

yorkw
sumber
9
Sepertinya itu mengalahkan tujuan menggunakan AsyncTask untuk memulai jika metode batas waktu ini berjalan pada utas UI utama ... Mengapa tidak menggunakan handlerpendekatan yang saya jelaskan dalam pertanyaan saya? Tampak jauh lebih mudah karena utas UI tidak membeku saat menunggu untuk menjalankan Handler's Runnable ...
Jake Wilson
Oke terima kasih, saya hanya tidak yakin apakah ada cara yang tepat untuk melakukannya atau tidak.
Jake Wilson
3
Saya tidak mengerti mengapa Anda memanggil get () di utas lain. Ini terlihat aneh karena AsyncTask sendiri adalah sejenis utas. Saya pikir solusi Jakobud lebih oke.
emeraldhieu
Menurut saya .get () mirip dengan join thread posix, yang tujuannya adalah untuk menunggu thread selesai. Dalam kasus Anda, ini berfungsi sebagai batas waktu, yang merupakan properti tidak langsung. Itulah mengapa Anda perlu memanggil .get () dari handler atau dari utas lain untuk menghindari pemblokiran UI utama
Constantine Samoilenko
2
Saya tahu ini bertahun-tahun kemudian, tetapi ini masih belum dibangun ke dalam android jadi saya membuat kelas pendukung. Saya harap ini membantu seseorang. gist.github.com/scottTomaszewski/…
Scott Tomaszewski
20

Dalam kasus ini, pengunduh Anda didasarkan pada untuk sambungan URL, Anda memiliki sejumlah parameter yang dapat membantu Anda menentukan batas waktu tanpa kode kompleks:

  HttpURLConnection urlc = (HttpURLConnection) url.openConnection();

  urlc.setConnectTimeout(15000);

  urlc.setReadTimeout(15000);

Jika Anda hanya membawa kode ini ke tugas asinkron Anda, tidak apa-apa.

'Read Timeout' adalah untuk menguji jaringan yang buruk selama proses transfer.

'Connection Timeout' hanya dipanggil di awal untuk menguji apakah server sudah aktif atau tidak.

Clement Soullard
sumber
1
Saya setuju Menggunakan pendekatan ini lebih sederhana, dan itu benar-benar mengakhiri koneksi saat batas waktu tercapai. Menggunakan pendekatan AsyncTask.get (), Anda hanya diberi tahu bahwa batas waktu telah tercapai, tetapi unduhan masih diproses, dan mungkin benar-benar selesai di lain waktu - menyebabkan lebih banyak kerumitan dalam kode. Terima kasih.
bingung
Perlu diketahui bahwa ini tidak cukup bila menggunakan banyak utas pada koneksi internet yang sangat lambat. Info lebih lanjut: kebocoranfromjavaheap.blogspot.nl/2013/12/… dan thushw.blogspot.nl/2010/10/…
Kapitein Witbaard
20

Gunakan Kelas CountDownTimer di samping kelas yang diperluas untuk AsyncTask dalam metode onPreExecute ():

Keuntungan utama, pemantauan Async dilakukan secara internal di dalam kelas.

public class YouExtendedClass extends AsyncTask<String,Integer,String> {
...
public YouExtendedClass asyncObject;   // as CountDownTimer has similar method -> to prevent shadowing
...
@Override
protected void onPreExecute() {
    asyncObject = this;
    new CountDownTimer(7000, 7000) {
        public void onTick(long millisUntilFinished) {
            // You can monitor the progress here as well by changing the onTick() time
        }
        public void onFinish() {
            // stop async task if not in progress
            if (asyncObject.getStatus() == AsyncTask.Status.RUNNING) {
                asyncObject.cancel(false);
                // Add any specific task you wish to do as your extended class variable works here as well.
            }
        }
    }.start();
...

ubah CountDownTimer (7000, 7000) -> CountDownTimer (7000, 1000) misalnya dan itu akan memanggil onTick () 6 kali sebelum memanggil onFinish (). Ini bagus jika Anda ingin menambahkan beberapa pemantauan.

Terima kasih atas semua saran bagus yang saya dapatkan di halaman ini :-)

Gilco
sumber
2

Saya tidak berpikir ada hal seperti itu yang dibangun ke dalam AsyncTask. Pendekatan Anda tampaknya bagus. Pastikan untuk memeriksa nilai isCancelled () secara berkala dalam metode doInBackground AsyncTask Anda untuk mengakhiri metode ini setelah thread UI membatalkannya.

Jika Anda ingin menghindari penggunaan penangan karena alasan tertentu, Anda dapat memeriksa System.currentTimeMillis secara berkala di dalam AsyncTask Anda dan keluar saat waktu tunggu, meskipun saya lebih menyukai solusi Anda karena ini benar-benar dapat mengganggu utas.

aleph_null
sumber
0
         Context mContext;

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

            //async task
            final RunTask tsk = new RunTask (); 
            tsk.execute();

            //setting timeout thread for async task
            Thread thread1 = new Thread(){
            public void run(){
                try {
                    tsk.get(30000, TimeUnit.MILLISECONDS);  //set time in milisecond(in this timeout is 30 seconds

                } catch (Exception e) {
                    tsk.cancel(true);                           
                    ((Activity) mContext).runOnUiThread(new Runnable()
                    {
                         @SuppressLint("ShowToast")
                        public void run()
                         {
                            Toast.makeText(mContext, "Time Out.", Toast.LENGTH_LONG).show();
                            finish(); //will close the current activity comment if you don't want to close current activity.                                
                         }
                    });
                }
            }
        };
        thread1.start();

         }
Mrityunjaya
sumber
2
Pertimbangkan untuk menambahkan beberapa penjelasan juga
Saif Asif
0

Anda dapat menempatkan satu ketentuan lagi untuk membuat pembatalan lebih kuat. misalnya,

 if (downloader.getStatus() == AsyncTask.Status.RUNNING || downloader.getStatus() == AsyncTask.Status.PENDING)
     downloader.cancel(true);
Vinnig
sumber
0

Menginspirasi dari pertanyaan saya telah menulis sebuah metode yang melakukan beberapa tugas latar belakang melalui AsyncTask dan jika pemrosesan membutuhkan waktu lebih dari LOADING_TIMEOUT maka dialog peringatan untuk mencoba lagi akan muncul.

public void loadData()
    {
        final Load loadUserList=new Load();
        loadUserList.execute();
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (loadUserList.getStatus() == AsyncTask.Status.RUNNING) {
                    loadUserList.cancel(true);
                    pDialog.cancel();
                    new AlertDialog.Builder(UserList.this)
                            .setTitle("Error..!")
                            .setMessage("Sorry you dont have proper net connectivity..!\nCheck your internet settings or retry.")
                            .setCancelable(false)
                            .setPositiveButton("Retry", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialogInterface, int i) {
                                    loadData();
                                }
                            })
                            .setNegativeButton("Exit", new DialogInterface.OnClickListener() {
                                @Override


                      public void onClick(DialogInterface dialogInterface, int i) {
                                System.exit(0);
                            }
                        })
                        .show();
            }
        }
    }, LOADING_TIMEOUT);
    return;
}
Abhishek
sumber