Android - ustawienie limitu czasu dla AsyncTask?


125

mam AsyncTask klasę, którą wykonuję, która pobiera dużą listę danych ze strony internetowej.

W przypadku, gdy użytkownik końcowy ma bardzo powolne lub przerywane połączenie danych w momencie użycia, chciałbym AsyncTaskustawić limit czasu po pewnym czasie. Moje pierwsze podejście do tego jest takie:

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 );

Po uruchomieniu uruchamiany AsyncTaskjest nowy program obsługi, który anuluje działanie AsyncTaskpo 30 sekundach, jeśli nadal działa.

Czy to dobre podejście? A może jest coś wbudowanego, AsyncTaskco lepiej nadaje się do tego celu?


18
Po wypróbowaniu kilku różnych podejść doszedłem do wniosku, że twoje pytanie powinno być zaakceptowaną odpowiedzią.
JohnEye

Dzięki za pytanie, naprawdę wpadłem w ten sam problem, a Twój fragment kodu pomógł mi +1
Ahmad Dwaik 'Warlock'

1
Twoje pytanie samo w sobie jest dobrą odpowiedzią +50 :)
karthik kolanji

Odpowiedzi:


44

Tak, istnieje AsyncTask.get ()

myDownloader.get(30000, TimeUnit.MILLISECONDS);

Zauważ, że wywołanie tego w głównym wątku (AKA. Wątek UI) zablokuje wykonanie, prawdopodobnie będziesz musiał wywołać to w osobnym wątku.


9
Wydaje się, że niweczy cel używania AsyncTask na początku, jeśli ta metoda limitu czasu działa w głównym wątku interfejsu użytkownika ... Dlaczego po prostu nie zastosować handlerpodejścia, które opisuję w moim pytaniu? Wydaje się o wiele prostsze, ponieważ wątek interfejsu użytkownika nie zawiesza się podczas oczekiwania na uruchomienie programu Handler Runnable ...
Jake Wilson,

Okej, dzięki, po prostu nie byłem pewien, czy istnieje właściwy sposób, aby to zrobić, czy nie.
Jake Wilson,

3
Nie rozumiem, dlaczego wywołujesz funkcję get () w innym wątku. Wygląda to dziwnie, ponieważ sam AsyncTask jest rodzajem wątku. Myślę, że rozwiązanie Jakobuda jest bardziej OK.
emeraldhieu

Myślę, że .get () jest podobne do łączenia wątku posix, którego celem jest oczekiwanie na zakończenie wątku. W twoim przypadku służy to jako limit czasu, który jest właściwością okolicznościową. Dlatego musisz wywołać .get () z programu obsługi lub z innego wątku, aby uniknąć blokowania głównego interfejsu użytkownika
Constantine Samoilenko

2
Wiem, że jest to lata później, ale nadal nie jest to wbudowane w Androida, więc stworzyłem klasę wsparcia. Mam nadzieję, że to komuś pomoże. gist.github.com/scottTomaszewski/…
Scott Tomaszewski

20

W przypadku, gdy Twój program do pobierania jest oparty na połączeniu z adresem URL, masz kilka parametrów, które mogą pomóc w zdefiniowaniu limitu czasu bez złożonego kodu:

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

  urlc.setConnectTimeout(15000);

  urlc.setReadTimeout(15000);

Jeśli po prostu wprowadzisz ten kod do zadania asynchronicznego, wszystko jest w porządku.

„Read Timeout” służy do testowania złej sieci podczas całego transferu.

„Limit czasu połączenia” jest wywoływany tylko na początku, aby sprawdzić, czy serwer działa, czy nie.


1
Zgadzam się. Użycie tego podejścia jest prostsze i faktycznie przerywa połączenie po osiągnięciu limitu czasu. Korzystając z podejścia AsyncTask.get (), jesteś tylko informowany, że limit czasu został osiągnięty, ale pobieranie jest nadal przetwarzane i może się zakończyć w późniejszym czasie - powodując więcej komplikacji w kodzie. Dzięki.
oszołomiony

Należy pamiętać, że to nie wystarczy, jeśli używasz wielu wątków na bardzo wolnych połączeniach internetowych. Więcej informacji: leakfromjavaheap.blogspot.nl/2013/12/… i thushw.blogspot.nl/2010/10/…
Kapitein Witbaard

20

Użyj klasy CountDownTimer po stronie rozszerzonej klasy AsyncTask w metodzie onPreExecute ():

Główną zaletą jest monitorowanie Async wykonywane wewnętrznie w klasie.

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();
...

na przykład zmień CountDownTimer (7000, 7000) -> CountDownTimer (7000, 1000) i wywoła onTick () 6 razy przed wywołaniem onFinish (). Jest to dobre, jeśli chcesz dodać monitorowanie.

Dzięki za wszystkie dobre rady, które otrzymałem na tej stronie :-)


2

Nie sądzę, by było coś takiego wbudowanego w AsyncTask. Twoje podejście wydaje się być dobre. Po prostu pamiętaj, aby okresowo sprawdzać wartość isCancelled () w metodzie doInBackground AsyncTask, aby zakończyć tę metodę, gdy wątek interfejsu użytkownika anuluje ją.

Jeśli z jakiegoś powodu chcesz uniknąć korzystania z programu obsługi, możesz okresowo sprawdzać System.currentTimeMillis w swoim AsyncTask i kończyć po przekroczeniu limitu czasu, chociaż bardziej podoba mi się twoje rozwiązanie, ponieważ może faktycznie przerwać wątek.


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();

         }

2
Rozważ również dodanie wyjaśnienia
Saif Asif

0

Możesz ustawić jeszcze jeden warunek, aby anulowanie było bardziej niezawodne. na przykład,

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

0

Inspirując się pytaniem, napisałem metodę, która wykonuje pewne zadania w tle za pośrednictwem AsyncTask i jeśli przetwarzanie zajmie więcej niż LOADING_TIMEOUT, pojawi się okno dialogowe z ostrzeżeniem, aby ponowić próbę.

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;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.