Android - встановлення тайм-ауту для AsyncTask?


125

У мене AsyncTaskклас, який я виконую, який завантажує великий список даних з веб-сайту.

У випадку, якщо кінцевий користувач на час використання має дуже повільний або плямистий зв’язок даних, я хотів би зробити AsyncTaskтайм-аут через певний проміжок часу. Мій перший підхід до цього виглядає так:

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

Після запуску програми запускається AsyncTaskновий обробник, який скасує AsyncTaskчерез 30 секунд, якщо він все ще працює.

Це хороший підхід? Або є щось вбудоване, AsyncTaskщо краще підходить для цієї мети?


18
Спробувавши кілька різних підходів, я дійшов висновку, що ваше питання має бути прийнятою відповіддю.
JohnEye

Дякую за запитання, що я дійсно потрапив у той самий випуск, і ваш фрагмент коду допоміг мені +1
Ахмад Двайк 'Warlock'

1
Ваше запитання сама по собі хороша відповідь +50 :)
karthik kolanji

Відповіді:


44

Так, є AsyncTask.get ()

myDownloader.get(30000, TimeUnit.MILLISECONDS);

Зауважте, що виклик цього в основному потоці (потік AKA. UI) заблокує виконання, можливо, вам потрібно буде викликати його в окремий потік.


9
Схоже, це перемагає мета використання AsyncTask для початку, якщо цей метод очікування працює на головному потоці інтерфейсу ... Чому б не просто використовувати handlerпідхід, описаний у моєму питанні? Здається набагато простіше, тому що потік інтерфейсу не замерзає під час очікування запуску програми "Обробник" ...
Джейк Вілсон,

Гаразд, дякую, я просто не був впевнений, чи правильно це зробити чи ні.
Джейк Вілсон

3
Я не розумію, чому ви викликаєте get () в іншій потоці. Це виглядає дивно, тому що сам AsyncTask є своєрідною ниткою. Я думаю, що рішення Якобуда більше в порядку.
смарагдовий

Я думаю .get () схожий на приєднання нитки posix, де мета - чекати завершення потоку. У вашому випадку він служить тайм-аутом, який є властивістю навколишнього середовища. Ось чому вам потрібно зателефонувати .get () з обробника чи з іншої теми, щоб уникнути блокування основного інтерфейсу
Костянтин Самойленко

2
Я знаю, що це через роки, але це все ще не вбудовано в android, тому я зробив клас підтримки. Я сподіваюся, що це комусь допоможе. gist.github.com/scottTomaszewski/…
Скотт Томашевський

20

У випадку, якщо ваш завантажувач базується на URL-з'єднанні, у вас є ряд параметрів, які можуть допомогти вам визначити час очікування без складного коду:

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

  urlc.setConnectTimeout(15000);

  urlc.setReadTimeout(15000);

Якщо ви просто вводите цей код у своє завдання асинхронізації, це нормально.

"Час очікування читання" - це перевірка поганої мережі під час передачі.

"Час очікування підключення" викликається лише на початку, щоб перевірити, чи працює сервер чи ні.


1
Я згоден. Використовувати такий підхід простіше, і він фактично припиняє з'єднання після досягнення тайм-ауту. Використовуючи підхід AsyncTask.get (), ви тільки інформуєте, що досягнуто обмеження часу, але завантаження все ще обробляється і може фактично завершитись у більш пізній час - викликаючи більше ускладнень у коді. Дякую.
запаморочив

Зауважте, що цього недостатньо при використанні безлічі потоків на дуже повільних підключеннях до Інтернету. Більше інформації: leakfromjavaheap.blogspot.nl/2013/12/… та thushw.blogspot.nl/2010/10/…
Kapitein Witbaard

20

Використовуйте CountDownTimer Class у бік розширеного класу для AsyncTask у методі onPreExecute ():

Основна перевага - моніторинг Async, виконаний всередині класу.

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

наприклад, змініть CountDownTimer (7000, 7000) -> CountDownTimer (7000, 1000), і він зателефонує onTick () 6 разів перед тим, як зателефонувати onFinish (). Це добре, якщо ви хочете додати деякий моніторинг.

Дякую за всі добрі поради, які я отримав на цій сторінці :-)


2

Я не думаю, що в AsyncTask є щось подібне. Ваш підхід здається вдалим. Просто не забудьте періодично перевіряти значення isCancegled () у методі doInBackground вашого AsyncTask, щоб закінчити цей метод, коли потік інтерфейсу скасує його.

Якщо ви хочете уникнути використання обробника з якихось причин, ви можете періодично перевіряти System.currentTimeMillis в межах вашого AsyncTask та виходити на час очікування, хоча мені подобається ваше рішення краще, оскільки воно може фактично перервати потік.


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
Розгляньте також додавання деяких пояснень
Сайф Асиф

0

Ви можете поставити ще одну умову, щоб зробити скасування більш надійним. наприклад,

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

0

Надихаючи на запитання, я написав метод, який виконує деякі фонові завдання через AsyncTask, і якщо обробка займає більше, ніж LOADING_TIMEOUT, тоді з’явиться діалог попередження для повторного повтору.

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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.