欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

很容易理解的Android AsyncTask源码与工作原理分析

程序员文章站 2022-04-10 09:46:50
AsyncTask定义及用法AsyncTask的使用方法AsyncTask的组成从源码分析AsyncTask的工作过程...

对于中小项目,会经常使用的AsyncTask,并且其中包含了线程池机制,Handler机制,任务排队等,设计确实比较巧妙,今天我们来进行一篇源码学习。这里没有太多的个人观点,主要针对源码进行说明。。。

其中会涉及到Handler相关的知识,如果对Handler了解的话应该能很容易理解,不了解的话可以读下我之前的这篇文章:很容易理解的Android消息机制分析

 

AsyncTask入门简介:

AsyncTask介绍

AsyncTask是一种轻量级的异步任务类,它可以执行后台任务并把任务执行的进度和结果传递给主线程进行ui的更新。AsyncTask是一个抽象的范型类,包含Params, Progress、Result三个范型参数。其中Params表示参数类型,Progress表示后台任务的执行进度的类型,而Result表示任务的返回结果的类型。如下:

@MainThread
public abstract class AsyncTask<Params, Progress, Result> {
    ...
}

 

AsyncTask抽象类的方法:

  • protected void onPreExecute() {
    }

    该方法运行在主线程。一半用于进行一些非耗时的初始化操作。

  •  @WorkerThread
     protected abstract Result doInBackground(Params... params);

    该方法在线程池中执行,用于执行异步任务。方法中的参数类型对应范型参数中的Params,方法的返回值类型对应范型参数中的Result。该方法运行在线程池中,用于执行耗时任务。在该方法中调用publishProgress,就会在主线程中收到onProgress的调用,可以进行任务进度的更新。

  • @MainThread
    protected void onProgressUpdate(Progress... values) {
    }

    该方法在主线程中执行,如doInBackground方法中描述那样,通过方法调用除法该方法。

  •  @MainThread
     protected void onPostExecute(Result result) {
     }

    在主线程中执行,在异步任务执行完成后执行该方法。其中的类型Result对应范型参数中的result,值是方法doInBackground的返回值。

AsyncTask的使用

再多的废话不如代码来的即直接。下边通过一个简单的小例子来说明一下AsyncTask的使用。简易代码,只是表达下大致使用过程,如果想直接使用线程的代码可以百度。。。谷歌。。。

public class MyDownloadTask extends AsyncTask<String, Integer, Integer>{

    public static final int STATUS_SUCCESS = 0;
    public static final int STATUS_PAUSE = 1;
    ...
    ...

    @override
    protected void onPreExecute(){
        //进行一些初始化操作
        OKHttpClient client = new OKHttpClicent();
        ...
    }

    @override
    protected Integer doInBackground(String... params){
        String downloadUrl = params[0];
        Request request = new Request.Builder()
                .url(downloadUrl);
        Response response = client.newCall(request).execute();
        InputSream is = null;
        try{
            is = response.body().byteStream();

            ....此处省略很多代码

            //从stream中读取数据,累加长度,计算progress,具体代码省略
            publishProgress(progress);
        }cache(Exception e){
        }
        return STATUS_SUCCESS;
    }

    @override
    protected void onProgress(Integer... values){
        //更新进度回调给下载发起者
        listener.onProgress(values[0]);
    }

    @override
    protected void onPostExecute(Integer status){
        if(status == STATUS_SUCCESS){
            listener.onSuccess();
        } else if(status == STATUS_PAUSE){
            //回调暂停
        }
        回调失败等其他状态。。。。
        ....
    }
}

调用AsyncTask

String downloadUrl = "...";
MyDownloadTask task = new MyDownloadtask();
task.execute(downloadUrl);

 

AsyncTask的原理

只会用还是不够的,作为一个有追求的码农。。。我们还是要看下其中的原理,防止别人问起的时候出现一问三不知的尴尬。。。相信不少小伙伴一提到源码二字就秒怂(比如我)。但是既然要学习,还是要硬着头皮上。。。。话不多少,开始!!

execute方法做了什么

通过上边对AsyncTask的使用的讲解,可以看到在调用AsyncTask的时候我们直接使用的方法就是它的execute方法。

 @MainThread
 public final AsyncTask<Params, Progress, Result> execute(Params... params) {
     return executeOnExecutor(sDefaultExecutor, params);
 }

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

//1. 这里是一个线程池类型的成员变量
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
    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;
    //2. 执行onPreExecute方法
    onPreExecute();

    //3. 处理参数
    mWorker.mParams = params;

    //4. 调用道这个方法,exec是一个线程池,对应的类是mFuture是一个包装的Runnable.
    exec.execute(mFuture);

    return this;
}

我们可以看到execute方法调用了executeOnExecutor方法,第一个参数是一个成员变量。该成员变量就是一个实例化的线程池,该线程池的execute会在注释4处被调用。在4之前,我们可以看到在2处直接调用AsyncTask的onPreExecute方法,因此该方法是在主线程直接执行的。接下来我们看一下注释4处对应的SerialExecutor对应的execute方法。

//这里从类名也反应了这个类的作用,Serial 主要用于排序
private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
        

        //1. SerialExecutor的execute方法的主要作用是对传进来的Runnable进行一个排序,塞到队列                
          mTasks中
        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                //2.在并发线程池中执行run方法
                public void run() {
                    try {
                        //3.执行原始的任务的run方法
                        r.run();
                    } finally {
                        //4.并发线程池执行一个任务后才进行下一个任务的获取
                        scheduleNext();
                    }
                }
            });
            //5. 如果队列为空,则执行scheduleNext方法
            if (mActive == null) {
                scheduleNext();
            }
        }
        
        //6. 该方法的作用是从队列中获取一个Runnable交给线程池执行
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

从上述代码可以看出,调用线程SerialExecutor的executor方法,主要是对任务Runnable进行一次排队,把任务放到mTasks队列中。然后判断mActive是否为null,如果为null说明AysncTask线程池还没有激活,则调用scheduleNext方法开始执行。我们可以从注释6处看到,scheduleNext方法从队列中获取一个任务,交给一个叫做“TREAD_POOL_EXECUTOR”的并发线程池进行执行。后续过程中,如果又调用了一个AsyncTask的execute,则会把Runnable继续加入到mTasks中。但是因为mActivit != null。所以不会在执行scheduleNext操作,那么新加入的线程是怎么执行的呢?刚才已经提到队列中的一个任务已经交给了并发线程池,该线程池会执行任务的run方法,对应的就是注释2处的run方法。这个run方法中,首先执行了原始任务的run方法,执行结束后通过scheduleNext方法再取出一条任务,交给并发执行线程执行。看到这里,相信大家应该明白过来了,这里的TREAD_POOL_EXECUTOR虽然是一个并发执行线程,但是它内部是完成一个任务的执行后,再通过scheduleNext方法让AsyncTask送进来下一条任务。因此实现了任务的排队串醒执行。

如果你能看到这里,恭喜,你已经了解了线程池内部串行执行的过程,不过你是否有一点疑问?为什么你创建的不同的AysncTask实例执行的任务都排到了一个队列中?

答案很简单:因为这里的SerialExecutor和TREAD_POOL_EXECUTOR对应的实例都是static类型的哇。所以不管你创建了几个AsyncTask实例,在方法去中都只有一份对应的实例。所以也只有一份mTAsks任务队列。

doInBackground执行探究

到此,我们直到我们的任务会按照排队顺序在线程池进行执行。那执行的到底是什么呢?我们接着往下走。

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    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();

    //1. 处理参数
    mWorker.mParams = params;
    //2. 送给队列
    exec.execute(mFuture);

    return this;
}

//3. 任务
private final FutureTask<Result> mFuture;

没错,我们又回到了这段代码,先看注释1处,这里是把我们调用时传入的参数赋值给mWork的mParams变量,先记住有这个东西。接着往下看,看完前边的分析,你肯定已经知道这个的作用。这里的mFuture就是送给任务队列的Runnable,并发线程池真正执行的run方法对应的原始任务就是这个mFuture。我们看下这个mFuture到底是什么东西,这里我把mFuture的声明粘贴到了注释3处。可以看到对应的类是FutureTask,定义如下:

public class FutureTask<V> implements RunnableFuture<V> {

    //....省略大部分代码,只看关键方法
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        //完结传入的一个回调
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

    public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            //2. 把回调赋值给c
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    //3.调用构造时传入的callback的call方法
                    result = 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);
        }
    }
    
}

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

我们可以看到,FutrureTask本质上就是一个Runnable,在run方法中回调了构造Future传入的callback。我们看看对Future的构造,位置在AsyncTask中:

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 occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };

我们可以看到传入future的是mWork这个变量,这个mWork的call方法做了什么呢?继续往下走。。。

mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    //1. 看这里看这里
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

我们可以看到,在注释1处调用了AysncTak的doInBackground方法。

看到这里我们先捋一下。。。。

并发线程池从队列中获取任务---->调用任务的run---->调用work的call---->调用AsyncTask的doInBackgroud

看到这里你应该知道了,AsyncTask的doInBackground在子线程中执行,其实是因为被送到了线程池中执行的。

分发消息的Handler

接下来我们看下进度更新的相关方法,在此之前,我们先看下AsyncTask中的handler成员

mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                //1.使用主线程中的Looper创建Handler,会在主线程中处理消息
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

正常情况下,我们在主线程调用AsyncTask无需给构造方法传入looper,对应的就是这里callbackLooper == null的情况。这种情况下,我们可以看到,注释1处生成了一个使用主线程消息循环的Handler。

更新进度在这里

在前边已经讲解,我们调用了publishProgress会触发onProgressUpdate。我们看下publishProgress。

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

从上述代码中我们可以看到,通过handler把消息抛了出去,因为handler是主线程中的handler,因此消息的执行是在主线程中执行的。我们看下对消息MESSAGE_POST_PROGRESS的处理。

private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @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;
                //1. 处理进度
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }

我们可以看到,在注释1处处理了进度,回调了onProgress方法。这里的result的task对应的就是AsyncTask。不需要去深究代码细节。

执行结束还是靠Handler

接下来,我们来看最后一个重要方法,onPostExecute。

mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }

依然是前边的mWorker成员变量的call方法,我们可以看到执行完doInbackgound后,在finally中执行了一个postResult方法,我们已经知道call调用是在线程池中发生的。那么接下来我们看postResult方法:

private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

通过该方法的实现我们可以看到,还是通过AsyncTask的handler把消息MESSAGE_POST_RESULT发送到了主线程处理。我们再来回顾下handler中的handleMessage方法,如下:

public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {

                //1.处理结果message
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                //2.处理进度message
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }

其中,注释2处我们在前边讲解处理进度的时候已经说到了。现在的处理结果的消息对应代码注释1,我们可以看到这里调用了AsyncTask的finish方法,finish方法是什么东西呢?我们再来挖掘一层。。。

  private void finish(Result result) {
        if (isCancelled()) {
            //1.取消
            onCancelled(result);
        } else {
            //2.执行完成
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

我们可以看到finish方法中,注释1调用了取消后的逻辑。如果没有取消,会正常回调onPostExecute方法。此时是在Handler对应的消息队列中处理的,该Handler对应的Looper是MainLoooper,因此,该方法执行是在主线程执行的。

两点补充:先上代码:

@MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                //1.异常处理
                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();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

补充1:通过以上分析我们已经知道,调用AsyncTask的execute方法,任务进行串行执行(该机制是从3.0开始,3.0之前主要是没有排队的过程,因此是并行执行)。那么现在我们能不能做到并行执行呢?答案是肯定的,我们只需要跳过排队的过程,直接调用上边代码中的executeOnExecutor方法,该方法是直接在线程池上执行任务。

补充2:在调用AsyncTask的时候,如果两次调用一个AsyncTask执行任务会根据当前任务的状态抛出异常。如下:

//该代码是错误示范,会导致异常
AsyncTask task = new MyAsyncTask();
task.execute("http://******.mp3");
//再次调用同一个task的execute
task.execute("http://*****.mp4");

该异常出现的原因可以在上边executeOnExecutor方法中看到,调用同一个任务的execute会走到异常模块,根据任务状态抛出对应的异常。

总结:对于AsyncTask的知识点重点有一下两点:

1、使用了SerialExecutor进行任务的排队。并通过scheduleNext集合try finally保证了消息串行执行。

2、使用Handler进行工作线程和主线程的切换。

本文地址:https://blog.csdn.net/xululu0401/article/details/102174567