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.