AsyncTask超时问题分析及解决

针对AsyncTask的超时处理
为什么要处理超时?
有些时候,AsyncTask后台处理超时,造成无法收到AsyncTask结果。虽然这种情况比较少出现,但我们必须要考虑到出现这种情况的处理。另外,要强调一点是,比较耗时的任务不宜使用AsyncTask,比较好的选择是使用线程池实现。
源码分析
先看AsyncTask相关源码:

  public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

从上面的代码中,可以看出在Asynctask构造里定义了两个对象,分别是WorkerRunnable和FutureTask对象。这里感兴趣的是FutureTask对象调用了get()方法,请看get()方法源码:

    /**
     * Waits if necessary for the computation to complete, and then
     * retrieves its result.
     *
     * @return The computed result.
     *
     * @throws CancellationException If the computation was cancelled.
     * @throws ExecutionException If the computation threw an exception.
     * @throws InterruptedException If the current thread was interrupted
     *         while waiting.
     */
    public final Result get() throws InterruptedException, ExecutionException {
        return mFuture.get();
    }

其中,mFuture是一个FutureTask对象,也就是调用的实际是FutureTask的get()方法。在调用get方法时,若任务取消会抛出一个 CancellationException 异常,若发生错误会抛出一个ExecutionException 异常,若当前线程被中断时会抛出一个InterruptedException 异常。

接下来看FutureTask中get方法实现:


    /**
     * @throws CancellationException {@inheritDoc}
     */
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

get()方法又进一步调用了awaitDone方法,并声明了两个异常InterruptedException, ExecutionException,

 /**
     * Awaits completion or aborts on interrupt or timeout.
     *
     * @param timed true if use timed waits
     * @param nanos time to wait, if timed
     * @return state upon completion
     */
    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        long last = timed ? System.nanoTime() : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            else if (timed) {
                long now = System.nanoTime();
                if ((nanos -= (now - last)) <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                last = now;
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }

简单来说,awaitDone在超时时间内执行任务当超过超时时间时会中止任务。进一步来说执行任务是从任务队列中移除任务的同时然后执行的。(详细此处不再分析)
超时问题解决方案:
回到上面的get方法,查看源码,还可以看到有一个get方法的重载:

 /**
     * @throws CancellationException {@inheritDoc}
     */
    public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        int s = state;
        if (s <= COMPLETING &&
            (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        return report(s);
    }

这个有参的get方法同上面无参的get方法一样,调用的是也是 get(long timeout, TimeUnit unit)方法。当调用有参的get方法时就相当于设置了超时时间,如果到达超时时间任务未执行完就会抛出一个TimeoutException异常。
而默认调用的是无参的get方法,即相当于asynctask未设置超时时间。

根据上面一系列的源码分析,我们可以在外部直接调用有参的get方法设置超时时间。
即:
解决方案一:

int TIME_OUT=3*1000;
try {
      AsyncTask(此处应该是它的实例化对象).execute().get(TIME_OUT, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
            e.printStackTrace();
    } catch (ExecutionException e) {
            e.printStackTrace();
    } catch (TimeoutException e) {
    //处理超时操作,如:弹出提示框,跳转页面等操作,处理界面的话需要通过handler否则会造成ANR     
        }        

解决方案二:
这种方法稍微有些麻烦,就是在asynctask中的doInBackground方法中定义一个定时器任务,当达到超时时间后,该定时器需要执行的操作是取消asynctask任务。使用Timer的 schedule (TimerTask task, Date when)实现。同时设置一个任务完成的标志位。当任务完成时就改变该标志位的值。接着在onpostExecute中根据标志位,做相应的处理,若超时则弹出提示框,页面跳转或是其他处理,否则就直接更新UI。

示例代码:

public class MainActivity extends Activity {
     private final static int TIME_OUT = 3 * 1000;
     private final static int SLEEP_TIME = 2 * 1000;

     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);

         setContentView(R.layout.main);                  
         new CancelSelfWhenTimeOutTask().execute();

     }

     private class CancelSelfWhenTimeOutTask extends AsyncTask<Void, Void, Void> {
         private boolean done = false;

         @Override
         protected Void doInBackground(Void... params) {
            cancelSelfWhenTimeOut();//定义一个定时器(Timer),若超过指定时间,则取消任务
            doTask();//执行耗时任务
            return null;
          }

        @Override
          protected void onPostExecute(Void result) {
             super.onPostExecute(result);
         //任务完成,发送handler更新ui
          }

         private void cancelSelfWhenTimeOut() {
     //当时间超过TIME_OUT时,若done标志位仍为false,则取消asynctask任务
             new Timer().schedule(new TimerTask() {
               @Override
               public void run() {
                    if (!done) {
                         CancelSelfWhenTimeOutTask.this.cancel(true);
                     }
                 }
             }, TIME_OUT);
         }

         private void doTask() {
            try {
                 //执行耗时任务
                 done = true;//改变标志位
             } catch (InterruptedException e) {
             }
         }
     }
   }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值