美文网首页
AsyncTask源码解析

AsyncTask源码解析

作者: 慕尼黑凌晨四点 | 来源:发表于2020-12-12 17:05 被阅读0次

    AsyncTask解析

    简介

    AsyncTask是一个专注于UI线程 和 后台线程 之间进行交流的辅助类。 一般用于 由后台线程发起指定计算任务,并将其结果展示在UI线程上 的场景。

    目前已被弃用(因为持有context引用,有可 能造成内存泄漏,缺失回调等原因),官方推荐采用 java.util.concurrent 或者 Kotlin协程 代替。但还是值得学习下。

    使用

    AsyncTask是一个抽象类,必须被子类继承使用。 继承后必须要实现doInBackground方法,里面写需要在后台执行的任务;通常还会实现onPostExecute,里面做UI界面想要做的操作。

     private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
         protected Long doInBackground(URL... urls) {
             int count = urls.length;
             long totalSize = 0;
             for (int i = 0; i < count; i++) {
                 totalSize += Downloader.downloadFile(urls[i]);
                 publishProgress((int) ((i / (float) count) * 100));
                 // Escape early if cancel() is called
                 if (isCancelled()) break;
             }
             return totalSize;
         }
    
         protected void onProgressUpdate(Integer... progress) {
             setProgressPercent(progress[0]);
         }
    
         protected void onPostExecute(Long result) {
             showDialog("Downloaded " + result + " bytes");
         }
     }
     
    

    执行AsyncTask

     new DownloadFilesTask().execute(url1, url2, url3);
    

    源码解析

    主要跟着源码梳理流程,我使用的版本是android-30。

    首先AsyncTask是一个抽象类,带了三个泛型:

    1. Params, 执行任务execute(Params p)时传入 的类型。
    2. Progress, 用来表示后台线程执行进度的 类型。
    3. Result,后台线程计算完后,返回结果的 类型.
    public abstract class AsyncTask<Params, Progress, Result> { 
        
        private static final int CORE_POOL_SIZE = 1;
        private static final int MAXIMUM_POOL_SIZE = 20;
        private static final int BACKUP_POOL_SIZE = 5;
        private static final int KEEP_ALIVE_SECONDS = 3;
        ...
    }
    

    而且看常量命名看的出来用到了线程池,核心线程1,最大线程20,维持3秒。属于即用即销的线程池,所以AsyncTask更适用于时间较短(几秒钟)的操作。

    execute

    直接看execute方法。@MainThread表示这个方法只能在主线程运行。

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
    
    • sDefaultExecutor【先混个面熟】

    sDefaultExecutor是一个静态全局的SerialExecutor。接着往下走。

    executeOnExecutor

    @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();
    
        mWorker.mParams = params;
        exec.execute(mFuture);
    
        return this;
    }
    

    先判断全局状态,一共有三种PENDING(待处理)、RUNNING(正在运行)和FINISHED(已结束)。必须要PENDING待处理才能往下走,然后把状态变更为RUNNING,接着执行onPreExecute,这是我们在自定义AsyncTask中要重写的方法,做执行前的准备工作。

    接着先把传进来的数据params存到一个叫mWork的东西中。 再执行exec.execute(mFuture);。【返回的是AsyncTask本身】


    exec.execute(mFuture);

    mFuture是啥?是个FutureTask(Runnable runnable),里面持有mWorker

    mFuture = new FutureTask<Result>(mWorker) { ... }
    

    mWorker是啥?是个继承自Callable名字叫WorkerRunnable的类。

    mWorker = new WorkerRunnable<Params, Result>(){ ... }
    

    WorkerRunnable继承自Callable,里面啥都没有,就存了个Params数组。

    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }
    

    刚才的params就是存到了这里的。


    exec就是DefaultExecutor

    所以要看上面第一步👆传来的静态全局常量sDefaultExecutor。定义为SerialExecutor

    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);
            }
        }
    }
    

    这个Executor内部有一个叫mTask的存储Runnable的双端队列【Deque:两边都可以进,都可以出】。

    调用execute方法无非就是往mTask里面存了个Runnable进去,这个Runnablerun()方法执行了我们传进来的mFuture的run方法。

    还有一个名叫mActive的runnable对象。刚进来肯定是null,所以走cheduleNext() ——从mTask中提一个出来,赋值给mActive,如果不为空,就把他(赋值后的mActive)放到线程池中去执行。

    mActive.run ---> mFuture.run() ---> mWork.call()

    一条线下来,我们要看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
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result);
            }
            return result;
        }
    };
    

    一眼就看到了result = doInBackground(mParams);,这里面是我们自己写的后台要处理的运算。

    然后把result post出去。

    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,getHandler返回的是一个内部的mHandler,在构造方法中被初始化的。

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

    默认为空的情况下得到的是getMainHandler()

        private static Handler getMainHandler() {
            synchronized (AsyncTask.class) {
                if (sHandler == null) {
                    sHandler = new InternalHandler(Looper.getMainLooper());
                }
                return sHandler;
            }
        }
    

    里面主要是加锁初始化这个自定义的Handler:InternalHandler

    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;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }
    

    InternalHandler接到消息

    回忆下上一步传递消息用的是MESSAGE_POST_RESULT,所以在handleMessage中得到消息后走的是result.mTask.finish(result.mData[0]);就是走task的finish方法。

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

    结束后会调用onPostExecute方法,传入result。然后我们在继承AsyncTask时继承这个方法,就得到了结果。

    publishProgress进度

    虽然一套流程走完了,但我们还是没看到怎么将进度更新到UI线程的。因为更新UI的起点不在AsyncTask源码中,而在用户自定义的AsyncTask中。对,就是那句publishProgress()方法。

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

    调用了这个方法后,传入进度,内部才会通过Handler将进度传递过来。代码还是在InternalHandler中,另一种情况:result.mTask.onProgressUpdate(result.mData);。 执行onProgressUpdate就又跳转到了用户的逻辑去了,实现了进度的传递。

    以上就是AsyncTask工作时的全部流程。

    面试问题

    AsyncTask是如何实现线程的调度的?

    AsyncTask.execute()方法实际是执行的SerialExecutor的execute方法,该方法做的事就是:向一个名为mTask的Runnable队列中插入Runnable,然后再取出、执行,取出、执行,取出、执行...直到队列为空为止。

    而执行用的是THREAD_POOL_EXECUTOR.execute(Runnable),这个Runnable是在线程池中执行的。而且这个Runnable是一个传入mWorkerFutureTask,所以最终执行的是mWorker【一个Callable】的call方法,call方法中放的doInBackground,所以可以保障doInBackground始终在后台运行。

    而返回的结果是通过Handler返回到了InternalHandler中,这个Handler传入的是Looper.getMainLooper()而且它是在AsyncTask初始化的时候 初始化的。这就保证了返回的onPostExecuteonProgressUpdate都是在主线程中执行的了。

    相关文章

      网友评论

          本文标题:AsyncTask源码解析

          本文链接:https://www.haomeiwen.com/subject/rsqygktx.html