前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AsyncTask 源码解析

AsyncTask 源码解析

作者头像
Yif
发布2019-12-26 14:47:48
4140
发布2019-12-26 14:47:48
举报
文章被收录于专栏:Android 进阶Android 进阶

AsyncTask 基本使用

Params

在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。

Progress

后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。

Result

当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。

因此,一个最简单的自定义AsyncTask就可以写成如下方式:

代码语言:javascript
复制
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
 
 ……
 
}

这里我们把AsyncTask的第一个泛型参数指定为Void,表示在执行AsyncTask的时候不需要传入参数给后台任务。第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位。第三个泛型参数指定为Boolean,则表示使用布尔型数据来反馈执行结果。 当然,目前我们自定义的DownloadTask还是一个空任务,并不能进行任何实际的操作,我们还需要去重写AsyncTask中的几个方法才能完成对任务的定制。

经常需要去重写的方法有以下四个:

  1. onPreExecute() 这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
  2. doInBackground(Params...) 这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
  3. onProgressUpdate(Progress...) 当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
  4. onPostExecute(Result) 当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

AsyncTask 源码分析

AsyncTask 前世今生

在android1.6以前,它是串行执行,android1.6开始采用线程池处理并行任务,从android3.0开始,采用串行执行任务,仍然可以调用executeOnExecute方法并行执行任务。Android 3.0之前,AsyncTask核心线程数为5个,最大核心线程数为128个,加上阻塞队列任务10个共容纳138个任务,当提交的任务超过138个时就会执行饱和策略,抛出异常。

AsyncTask 线程池配置的参数

  1. 核心线程数等于CPU核心数+1
  2. 最大线程数为CPU核心数2倍+1
  3. 核心线程无超时时长,非核心线程超时时长为1秒
  4. 任务队列容量128

AsyncTask 线程池分析

AsyncTask内部有两个线程池(SerialExecutorTHREAD_POOL_EXECUTOR)和一个IntentHandler, SerialExecutor用于任务排队,THREAD_POOL_EXECUTOR用于真正执行任务,IntentHandler用于将执行环境从线程池中切换到主线程。

创建AsynacTask并初始化必须在主线程中执行

代码语言:javascript
复制
/** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */
public AsyncTask() {   
this((Looper) null);}/** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. * * @hide */
public AsyncTask(@Nullable Handler handler) {   
this(handler != null ? handler.getLooper() : null);
}

创建一个异步任务AsyncTask

代码语言:javascript
复制
public AsyncTask(@Nullable Looper callbackLooper) {
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()

getMainHandler方法并初始化InternalHandler? getMainHandler() : new Handler(callbackLooper);

代码语言:javascript
复制
 
​    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;
        }
    };
 
    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);
            }
        }
    };
}
private static Handler getMainHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler(Looper.getMainLooper());
        }
        return sHandler;
    }
}

sHandler是一个静态变量,将执行环境切换到主线程,这就要求sHandler在主线程中创建,静态成员变量会在加载类时进行初始化变相要求AsyncTask在主线程中加载

代码语言:javascript
复制
private static InternalHandler sHandler;
private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {
        postResult(result);
    }
}
 
private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
//发送消息切换到主线程中执行
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}
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
//执行finish方法
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
//执行onProgressUpdate方法
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
private void finish(Result result) {
//如果当前任务取消了,就调用onCancelled,否则调用onPostExecute方法
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}
//doBackground方法调用publishProgress方法从子线程切换到主线程进行UI更新
@WorkerThread
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

SerialExecutor源码分析

代码语言:javascript
复制
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;
 
    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }
 
    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

SerialExecutor 总结

SerialExecutor使用ArrayQueue来管理Runnable对象,当一次性启动很多任务时,首先第一次运行executor方法,会调用ArrayQueue将任务添加到队列尾部,并判断mActive是否为空,为空调用scheduleNext方法,这个方法会从队列头部取值,然后赋值给mActive,然后调有THREAD_POOP_EXECUTOR取出并执行runnable对象。之后又有新任务,将会调用offer将runnable传入队列尾部,此时mActivie不为空,就不调用scheduleNext方法。

每当任务执行完毕后才会执行下一个任务,SerialExecutor模仿的是单一线程池操作,同一时刻只会有一个任务执行。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年7月16日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • AsyncTask 基本使用
    • Params
      • Progress
        • Result
        • AsyncTask 源码分析
          • AsyncTask 前世今生
            • AsyncTask 线程池配置的参数
              • AsyncTask 线程池分析
                • SerialExecutor 总结
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档