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
Ваш вопрос сам по себе является хорошим ответом +50 :)
karthik kolanji

Ответы:

44

Да, есть AsyncTask.get ()

myDownloader.get(30000, TimeUnit.MILLISECONDS);

Обратите внимание, что, вызывая это в основном потоке (AKA. UI thread), вы блокируете выполнение. Возможно, вам потребуется вызвать его в отдельном потоке.

yorkw
источник
9
Похоже, что это противоречит цели использования AsyncTask для начала, если этот метод тайм-аута выполняется в основном потоке пользовательского интерфейса ... Почему бы просто не использовать handlerподход, который я описываю в моем вопросе? Кажется намного более простым, потому что поток пользовательского интерфейса не зависает в ожидании запуска Runnable обработчика ...
Джейк Уилсон
Хорошо, спасибо, я просто не был уверен, есть ли правильный способ сделать это или нет.
Джейк Уилсон,
3
Я не понимаю, почему вы вызываете get () в другом потоке. Это выглядит странно, потому что AsyncTask сам по себе является своего рода потоком. Я думаю, что решение Якобуда более подходящее.
emeraldhieu 08
Я думаю, что .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 (), вам сообщают только о том, что лимит времени был достигнут, но загрузка все еще обрабатывается и может фактически завершиться позже, что вызывает дополнительные сложности в коде. Спасибо.
dazed
Имейте в виду, что этого недостаточно при использовании большого количества потоков при очень медленном интернет-соединении. Дополнительная информация: leakfromjavaheap.blogspot.nl/2013/12/… и thushw.blogspot.nl/2010/10/…
Kapitein Witbaard 02
20

Используйте класс CountDownTimer в дополнение к расширенному классу для 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 (). Это хорошо, если вы хотите добавить мониторинг.

Спасибо за все хорошие советы, которые я получил на этой странице :-)

Gilco
источник
2

Я не думаю, что в AsyncTask есть что-то подобное. Ваш подход кажется хорошим. Просто не забудьте периодически проверять значение isCancelled () в методе doInBackground вашего AsyncTask, чтобы завершить этот метод, как только поток пользовательского интерфейса отменит его.

Если вы по какой-то причине хотите избежать использования обработчика, вы можете периодически проверять System.currentTimeMillis в своей AsyncTask и выходить по таймауту, хотя мне больше нравится ваше решение, поскольку оно может фактически прервать поток.

aleph_null
источник
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);
Vinnig
источник
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;
}
Абхишек
источник