美文网首页
AsyncTask 完全解析

AsyncTask 完全解析

作者: ITRenj | 来源:发表于2021-03-07 00:36 被阅读0次

    AsyncTask 完全解析

    引言

    我们知道,在 Android 中,UI 线程不能执行耗时操作;在子线程中不能执行更新 UI 的操作。如果需要在子线程中进行 UI 相关的操作,那么就需要借助 Android 中的异步消息机制(比如我们熟悉的Handler消息机制)才能完成。如果我们自己写代码来进行相关的处理,不仅麻烦,而且大部分的代码都是相同的,AsyncTask 的出现就能帮我们省去大部分的代码,并且可以非常灵活方便地从子线程切换到UI线程。今天就从 AsyncTask 的简单使用到源码进行解析,一步一步看看他是怎么实现的。

    AsyncTask 的简单使用

    创建 AsyncTask 实现类

    class DownLoadAsyncTask extends AsyncTask<String, Integer, Boolean> {
        @Override
        protected Boolean doInBackground(String... params) {
            // 运行在子线程,执行访异步任务
            Log.i("DownLoadAsyncTask", "doInBackground(): 开始执行异步任务");
            // publishProgress(Integer); // 调用更新进度方法
            return downLoadImage(params[0]); // 下载图片方法
        }
    
        @Override
        protected void onPreExecute() {
            // 运行在主线程,异步任务执行前的准备工作
            Log.i("DownLoadAsyncTask", "onPreExecute(): 执行异步任务前的准备工作");
            super.onPreExecute();
        }
    
        @Override
        protected void onPostExecute(Boolean result) {
            // 运行在主线程,异步任务执行完成之后的回调
            Log.i("DownLoadAsyncTask", "onPostExecute(): 异步任务执行完成之后的回调 => " + result);
            super.onPostExecute(result);
        }
    
        @Override
        protected void onProgressUpdate(Integer... progresses) {
            // 运行在主线程,用于更新进度
            // 注意:该方法需要在doInBackground()内部调用publishProgress()方法并传递进度,否则不会执行
            Log.i("DownLoadAsyncTask", "onProgressUpdate(): 用于更新进度");
            super.onProgressUpdate(progresses);
        }
    }
    

    AsyncTask<String, Integer, Boolean> 泛型说明:

    1. 第一个泛型:doInBackground() 方法的参数类型
    2. 第二个泛型:onProgressUpdate() 方法的参数类型
    3. 第三个泛型:doInBackground() 方法的返回类型 和 onProgressUpdate() 方法的参数类型

    开始执行任务

    String url = "https://www.baidu.com/img/bd_logo1.png";
    DownLoadAsyncTask downLoadAsyncTask = new DownLoadAsyncTask();
    downLoadAsyncTask.execute(url);
    

    以上就是 AsyncTask 的基本用法。

    源码解析

    构造方法

    public AsyncTask() {
        this((Looper) null);
    }
    
    public AsyncTask(@Nullable Handler handler) {
        this(handler != null ? handler.getLooper() : null);
    }
    
    /**
     * @hide
     */
    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
    
        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);
                }
            }
        };
    }
    

    构造方法说明:

    1. 最终调用的都是AsyncTask(@Nullable Looper callbackLooper)方法,但是这个方法使用了@hide注解,开发者并不能直接调用。构造方法虽然比较长,但是他并没有任何的操作被执行,只是初始化了mHandlermWorkermFuture几个变量,并且将 mWorker 对象作为 mFuture 的构造方法参数传递给了 mFuture 对象。
    2. mHandler 是MainHandler
    3. 有一点需要注意:构造方法只能在UI线程中调用

    构造方法中用到的部分对象说明:

    1. mHandler:Android Handler消息机制中的Handler对象,这里是InternalHandler对象,暂时记住就行了,后面会粘贴出具体实现的代码和说明

      class InternalHandler extends Handler:

    2. mWorker:实现 Callable 接口的 AsyncTask 类的内部类 WorkerRunnable
    3. mFuture:FutureTask 对象。FutureTask 对象的继承关系
      1. class FutureTask<V> implements RunnableFuture<V>
      2. interface RunnableFuture<V> extends Runnable, Future<V>

    执行异步任务方法

    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);// 调用内部方法
    }
    
    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; // 将状态设置为RUNNING
    
        onPreExecute(); // 回调异步任务执行前的准备工作方法
    
        mWorker.mParams = params; // 将传递的参数赋值给 mWorker 对象的 mParams 字段
        exec.execute(mFuture); // 执行 mFuture
    
        return this;
    
    }
    

    到这一步的时候,我们的任务已经切换子线程中了。

    方法说明:

    1. 判断当前的状态,如果是RUNNING或者是FINISHED就抛出异常
    2. 执行回调方法 onPreExecute() ,执行异步任务前的准备工作,比如显示进度对话框等
    3. mFuture 对象 交给 exec.execute(mFuture) 执行

    exec.execute(mFuture)说明:

    exec:即 sDefaultExecutor;实现 Executor接口的内部类 SerialExecutor

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    
    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(); // 调用参数 Runnable 对象的 run() 方法
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }
    
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
    

    通过上面 SerialExecutor 类的定义,我们可以看到 execute() 方法调用了它的参数(即mFuture)的run()方法,我们继续查看mFuturerun() 方法

    public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            runner = null;
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
    

    在该方法中主要是调用了 callable 对象的 call() 方法,我们一起看看 callable对象是从哪里来的。

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
    

    查看 FutureTask 对象的构造方法,我们发现callable对象是在构造方法中初始化的。好,我们现在就只要知道 FutureTask 对象是在哪里初始化的,就知道 call() 方法的具体执行内容是什么了。那么你是否还记得我们在查看 AsyncTask 类的构造方法时看到了它初始化几个变量就有FutureTask 对象,所以在这里执行的 call() 方法具体的方法体就是在AsyncTask 类的构造方法总中定义的方法体。为了方便观察,我把这段代码在粘贴一次:

    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); // 执行 doInBackground 回调方法,参数在mParams的值在executeOnExecutor()中初始化了
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result); // 设置结果
                }
                return result;
            }
        };
    

    看了这么久的代码,我们总算看到了执行任务的方法,也就是我们的重写方法 doInBackground(mParams) 返回的 Result 是结果泛型类型(第三个泛型的类型),在上面说过了三个泛型表示的意义;也就是说,到了这一步,我们的异步任务真正的开始执行了;我们接着看finally中的代码,调用的 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;
    }
    

    在这个方法中使用了一个新的类 AsyncTaskResult,它也是 AsyncTask 的内部类,定义如下:

    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;
    
        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
    

    回过头在看 postResult(Result result) 这个方法中的代码非常简单,getHandler()方法获取到我们在构造方法中初始化的 mHandler 对象,接着获取一个 Message 对象并且创建一个 AsyncTaskResult 对象(这个对象包含了AsyncTask 类的本类对象和 doInBackground()方法返回的结果对象2个参数)作为消息的内容,最后将这个消息发送到目标 mHandler 。上面说过了这个mHandlerInternalHandler,但是没有贴出具体实现的代码,那么现在我们就来看一下它到底做了些什么(InternalHandler类是AsyncTask类的内部类):

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

    这段代码我想大家都能看懂吧,最常见的 Handler 写法,通过判断 Messagewhat 进行不同的操作(任务执行完成或者更新进度)。我们只需要弄清楚 result.mTask 表示的哪一个对象就可以知道调用的方法是什么了。
    postResult(Result result)方法中,我们知道了 Messageobj 字段的值是 AsyncTaskResult 对象,参数是 thisdoInBackground()方法返回的结果对象,很容易知道this表示 AsyncTask 本类对象;在通过 AsyncTaskResult 的构造方法我就知道 result.mTask 就是AsyncTask 的本类对象,所以我们就知道了 result.mTask.finish(result.mData[0]) 方法就是AsyncTask 类的 finish(Result result) 方法了;同理result.mTask.onProgressUpdate(result.mData) 方法就是AsyncTask 类的 onProgressUpdate(Progress... values) 方法。

    看看AsyncTask 类的 finish(Result result) 方法:

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

    方法说明:

    1. 判断是不是取消了,如果取消了,就回调 onCancelled() 方法(开发者重写)
    2. 如果没有取消就回调 onPostExecute(Result result) 方法(开发者重写)
    3. 将任务的执行状态改变为 FINISHED

    到此为止,我们发现的任务执行前的准备方法onPreExecute()、任务执行方法 doInBackground 以及 取消方法 onCancelled() 和完成方法 onPostExecute() 都成功回调了;但是我们更新进度的方法 onProgressUpdate() 却并没有在哪里有调用;如果看的比较知仔细的可能已经发现了,我在第一部分简单使用中说明了:

    更新进度回调.png

    那么我们看看 publishProgress(Progress... values) 方法:

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

    它和 postResult(Result result) 方法是一样的,发送一个消息,就不在多说了,回过头看一下就知道了。

    以上就是 AsyncTask 的主要执行过程。

    相关文章

    相关文章

      网友评论

          本文标题:AsyncTask 完全解析

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