Android线程相关工具之AsyncTask

457 阅读3分钟

「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」。

AsyncTask

在android中由于处理任务的需要,对线程这一概念进行了进一步地封装,为了更快速地响应用户的交互,在android中将线程分为主线程和子线程,通常4大组件的运行都是在主线程中,这也就是相关组件处理事件时间不能过长的原因,子线程的任务主要是在后台处理一些耗时比较长的任务,比如网络请求以及I/O操作等
AsyncTask其实是对Handler的进一步封装,这点在接下来的分析中可以体现出来
AsyncTask是一个抽象泛型类

public abstract class AsyncTask<Params, Progress, Result>

简单可以理解为Params是输入类型,Result是输出类型,Progress是表示进度的类型
该抽象类有以下几个主要的方法

  • execute()触发异步任务的执行
  • onPreExecute()这个方法在主线程中执行,主要用于在任务执行之前的一些准备工作
  • protected abstract Result doInBackground(Params... params);处理异步任务,这个方法有两个事情要做,一个是将进度通过PublishProgress方法通知给onProgressUpdate方法进行进度的更新,二是将处理结果传递给onPostExecute方法
  • protected void onProgressUpdate(Progress... values)任务进度更新
  • protected void onPostExecute(Result result) 结果输出

由于AsyncTask类大部分是未实现的方法,接下来我们通过具体的例子来分析AsyncTask的工作流程

class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
    @Override
    protected Boolean doInBackground(Void... voids) {
        for(int i = 0; i <= 100; i++){
            publishProgress(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(isCancelled())break;
        }
        return true;
    }
}
DownloadTask downloadTask = new DownloadTask();
downloadTask.execute();

这是一个实现后台下载的任务,启动任务的方法是实例化一个downloadTask,接着调用它的execute方法。 接着我们顺着execute方法去梳理AsyncTask的工作流程

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

从方法名executeOnExecutor可以看出任务交给了线程池,这一步在主线程中执行

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
...
    onPreExecute();

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

    return this;
}

这里onPreExecute方法出现了,之前说过这个方法会在异步任务执行之前去做一些相关的预处理。 接着看execute方法,这是Executor接口方法,我们到实现Executor类中去看,这个类是sDefaultExecutor对象所属的类,查看发现是SerialExecutor

public synchronized void execute(final Runnable r) {
    mTasks.offer(new Runnable() {
        public void run() {
            try {
                r.run();
            } finally {
                scheduleNext();
            }
        }
    });
    if (mActive == null) {
        scheduleNext();
    }
}

可以发现,这里是将任务进一步封装后插入了mTasks中

protected synchronized void scheduleNext() {
    if ((mActive = mTasks.poll()) != null) {
        THREAD_POOL_EXECUTOR.execute(mActive);
    }
}

同时我们也可以发现如果mActive == null 也就是说没有任务正在执行,那么就会让线程池去执行下一个任务,并且每个任务结束之后也会去调用下一个任务
接着我们回到Runnable,由上面分析最终我们执行的是这个Runnable,通过追踪Runnable的来源,我们发现

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的东西,可以发现doInBackground就是我们自己要具体实现的方法,结果result最终通过postResult传递出去,注意doInBackground是运行在线程池中的,我们的结果最终要交给主线程,因此postResult必有线程切换

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

果然,这里是采用Handler的机制去传递信息的,至于Handler的实例化,我们可以看下面代码

public AsyncTask() {
    this((Looper) null);
}
public AsyncTask(@Nullable Looper callbackLooper) {
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
        ? getMainHandler()
        : new Handler(callbackLooper);
}

我们之前实例化AsyncTask时调用的是空参构造,因此这里默认使用主线程的Handler