Android进阶系列10-AsyncTask分析

博主的上篇文章Android进阶系列9-Android异步消息处理Handler机制解析中介绍了Android异步消息处理机制相关的Handler、MessageQueue、Looper。今天再来看一个简便的在子线程切换到UI线程的官方类AsyncTask,借此加深下对异步消息处理的理解。
插播一段,博主至今写了十几篇博文,看了几十篇甚至上百篇博文,从怎样更容易理解的角度看,比较好的方式是从用法开始,一步步地去追踪内部调用过程,这样理解起来清晰易懂。郭婶是这方面的杰出代表,博主又一次在博文中夸郭婶了。。。
本文的诞生,要感谢这几篇文章 Android AsyncTask完全解析,带你从源码的角度彻底理解Android源码分析—带你认识不一样的AsyncTask Android异步任务处理框架AsyncTask源码分析
本文分为两部分:从调用的角度一步步地看代码;从使用注意点说开去。

AsyncTask调用过程

AsyncTask的使用从继承AsyncTask类开始,创建该类的对象,并调用execute()方法准备执行。
先看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);  
                }  
            }  
        };  
    }  

初始化了mWorker和mFuture变量,并在初始化mFuture的时候将mWorker作为参数传入。mWorker是一个Callable对象,mFuture是一个FutureTask对象(Callable和FutureTask的含义在后续的文章中,博主再做详细解释),这两个变量在后续调用中会用到,稍后再看。接下来就是execute()方法:

/*调用一个可变参方法*/
public final AsyncTask<Params, Progress, Result> execute(Params... params) {  
    return executeOnExecutor(sDefaultExecutor, params);  
}
/*executeOnExecutor方法*/
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
     /*这段if判断使得一个AsyncTask对象只能execute()一次,否则会抛出相应的异常*/
     if (mStatus != Status.PENDING) {
         switch (mStatus) {
             case RUNNING:
                  throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
             case FINISHED:
                  throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
         }
     }
     mStatus = Status.RUNNING;
     /*调用到重写的onPreExecute方法*/
     onPreExecute();
     mWorker.mParams = params;
     exec.execute(mFuture);
     return this;
}  

启动一个异步任务后,从AsyncTask的execute方法调用executeOnExecutor方法,executeOnExecutor的exec.execute(mFuture)方法是执行的关键(其他代码的解释见注释),看下它涉及到的代码,exec变量源于sDefaultExecutor,sDefaultExecutor 是SerialExecutor对象,SerialExecutor的execute方法如下:

......
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();  
......
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;  
......
public synchronized void execute(final Runnable r) {  
     /*将新的AsyncTask任务加入到双向队列中*/  
     mTasks.offer(new Runnable() {  
     public void run() {  
          try {  
              /*执行AsyncTask任务*/  
              r.run();//FutureTask类的run()方法  
          } finally {  
          /*当前AsyncTask任务执行完毕后,进行下一轮执行,如果还有未执行任务的话*/
          scheduleNext();  
          }  
       }  
     });  
     /*如果当前没有任务在执行,直接进入执行逻辑*/  
     if (mActive == null) {  
         scheduleNext();  
     }  
}
protected synchronized void scheduleNext() {  
     //从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行  
      if ((mActive = mTasks.poll()) != null) {  
          THREAD_POOL_EXECUTOR.execute(mActive);  
      }  
 }    
......
/*并发池初始化*/
public static final Executor THREAD_POOL_EXECUTOR  
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,  
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);  

SerialExecutor的execute方法调用scheduleNext方法获取任务,交由并发池执行。任务的具体的内容在r.run()方法内部。FutureTask的run方法源码如下:

public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;//c由callable赋值
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();//run方法中执行c.call方法
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
/*callable初始化于FutureTask构造函数*/
public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;//由AsyncTask构造函数可以知道callable由mWorker赋值
        this.state = NEW;       // ensure visibility of callable
    }

结合注释知道,c.call()方法是mWorker的call方法,它通过return postResult(doInBackground(mParams)); 调用了doInBackgroud方法,说明AsyncTask的doInBackgroud方法确实在子线程中执行。想必调用doInBackgroud的postResult方法应该就是把子线程的执行结果发送到主线程中。

private Result postResult(Result result) {  
    Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
            new AsyncTaskResult<Result>(this, result));  
    message.sendToTarget();  
    return result;  
}  

这段代码很亲切了,sHandler创建一个消息并发送给了MessageQueue。看下sHandler会如何处理这个msg:

private static class InternalHandler extends Handler {  
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})  
    @Override  
    public void handleMessage(Message msg) {  
        AsyncTaskResult result = (AsyncTaskResult) msg.obj;  
        switch (msg.what) {  
            case MESSAGE_POST_RESULT:  
                // There is only one result  
                result.mTask.finish(result.mData[0]);  
                break;  
            case MESSAGE_POST_PROGRESS:  
                result.mTask.onProgressUpdate(result.mData);  
                break;  
        }  
    }  
}  

很容易看出来:doInBackgroud之后会结束任务,否则调用onProgressUpdate刷新。看下finish代码:

  private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

如果是取消任务,就调用onCancelled方法,否则调用onPostExecute方法。而handleMessage中的MESSAGE_POST_PROGRESS条件出发在publishProgress方法中:


protected final void publishProgress(Progress... values) {  
    if (!isCancelled()) {  
        sHandler.obtainMessage(MESSAGE_POST_PROGRESS,  
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();  
    }  
}  

同样的,利用sHandler实现子线程切换到主线程执行。至此我们知道doInBackgroud的结果如何传回UI线程以及publishProgress方法如何在自线程中操纵UI线程。借助了简单的Handler机制,封装了一套简单易用的代码给了开发者。
AsyncTask的执行示意图如下:

AsyncTask使用注意点

可能有人会想,AsyncTask这么好,有没有啥缺点呢?嗯,good question。从上文的代码分析中,不知道大家有没有注意到这几个问题:

  1. SerialExecutor是利用scheduleNext方法顺序获取任务再执行的,当有多个任务时,AsyncTask无法并行执行。
  2. SerialExecutor添加于Android 3.0之后,早期的AnsycTask存在一个问题:AnsycTask建立一个同一时刻能够运行的线程数为5,线程池总大小为128的线程池。也就是说当我们启动10个任务时,只有5个任务能够立刻执行,另外的5个任务则需要等待,当有一个任务执行完毕后,第6个任务才会启动,以此类推。而线程池中最大能存放的线程数是128个,如果尝试去添加第129个任务,程序就会崩溃。
  3. SerialExecutor是默认线程池,但可以在executeOnExecutor方法调用时,重新初始化exec变量,比如:

    (new AsyncTask()).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");

这样就可以做到真正的并发执行AysncTask任务,而不是挨个任务取出来扔到并发池里。

总结

使用AsyncTask的规则(这些都在AsyncTask的注释文档中可以看到),或者大家可以参考 Android源码分析—带你认识不一样的AsyncTask这篇文章的注释。

  1. AsyncTask的类必须在UI线程加载(从4.1开始系统会帮我们自动完成)

  2. AsyncTask对象必须在UI线程创建

  3. execute方法必须在UI线程调用

  4. 不要在你的程序中去直接调用onPreExecute(), onPostExecute, doInBackground, onProgressUpdate方法

  5. 一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常

  6. AsyncTask不是被设计为处理耗时操作的,耗时上限为几秒钟,如果要做长耗时操作,强烈建议你使用Executor,ThreadPoolExecutor以及FutureTask

  7. 在1.6之前,AsyncTask是串行执行任务的,1.6的时候AsyncTask开始采用线程池里处理并行任务,但是从3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程来串行执行任务

很惭愧,做了一点微小的贡献!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值