美文网首页
AsyncTask知识梳理

AsyncTask知识梳理

作者: 小和尚恋红尘 | 来源:发表于2018-09-26 09:09 被阅读0次

    上一篇我们讲解了Handler的相关知识,这篇文章就在来对AsyncTask进行讲解。
    那么,什么是AsyncTask呢?AsyncTask是一种轻量级的异步线程类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程更新UI。简单来说AsyncTask也就是对Handler和线程池的封装。

    我们先来看下AsyncTask的一个简单例子:

    1        AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {
    2            @Override
    3            protected String doInBackground(String... params) {
    4                return params[0];
    5            }
    
    6            @Override
    7            protected void onProgressUpdate(Integer... values) {
    8                progressBar.setProgress(values[0]*100/1000);
    9            }
    
    10            @Override
    11            protected void onPostExecute(String s) {
    12                Log.e("LHC", "result:"+ s); 
    13            }
    
    14            @Override
    15            protected void onPreExecute() {
    16                //TODO 
    17            }
    18        };
    
    19        asyncTask.execute("AsyncTask 测试");
    
    • 1行:生成了一个AsyncTask类对象,而AsyncTask是一个抽象类,具体定义为AsyncTask<Params, Progress, Result>,其中参数Params为在执行任务时,发送给任务的参数类型,也就是方法doInBackground的参数类型;参数Progress为后台执行期间的进度类型,也就是方法onProgressUpdate的参数类型;参数Result为后台执行的结果类型,也就是方法onPostExecute的参数类型。
    • 2~5行:doInBackground(Params... params)方法,在方法中定义后台线程要执行的任务,参数在执行任务也就是调用execute(Params... params)时传入;在方法中可以执行publishProgress(Progress... values)方法,用来更新UI。
    • 6~9行:onProgressUpdate(Progress... values)方法,在UI中用来更新任务进度,由publishProgress(Progress... values)方法内部进行调用。
    • 10~13行:onPostExecute(Result result)方法,在UI线程中用于保存或者显示结果。参数resultdoInBackground执行后的返回结果。
    • 14~17行:onPreExecute()方法,用于做一些准备工作。在doInBackground方法之前调用。
    • 19行:execute(Params... params)方法,执行任务,params就是要传入的参数值。

    这样就完成了AsyncTask的简单使用,下面我们就看看源码内部的实现。看第1行代码new AsyncTask<String, Integer, String>()中的代码实现:

        /**
         * 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 Looper callbackLooper) {
    1       mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
                ? getMainHandler()
                : new Handler(callbackLooper);
    
    2       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
    3                   result = doInBackground(mParams);
                        Binder.flushPendingCommands();
                    } catch (Throwable tr) {
                        mCancelled.set(true);
                        throw tr;
                    } finally {
    4                   postResult(result);
                    }
                    return result;
                }
            };
    
    5       mFuture = new FutureTask<Result>(mWorker) {
                @Override
                protected void done() {
                    try {
    6                   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) {
    7                    postResultIfNotInvoked(null);
                    }
                }
            };
        }
    

    需要注意的是,代码中的两个构造方法都需要在主线程中运行,看两个方法的注释。代码最终调用到了带参数的AsyncTask(@Nullable Looper callbackLooper)构造函方法,具体分析如下:

    • 1行:根据传入的callbackLooper参数是否为空或者是否为Looper.getMainLooper()也就是主线程的Looper值,来生成一个Handler对象;当为空或者值相等时,调用了getMainHandler()方法;当不为空或者值不相等时,调用了new Handler(callbackLooper)
      getMainHandler()方法,具体代码为:
        private static Handler getMainHandler() {
            synchronized (AsyncTask.class) {
                if (sHandler == null) {
                    sHandler = new InternalHandler(Looper.getMainLooper());
                }
                return sHandler;
            }
        }
    
        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类对象,传入的是主线程的Looper值。在看InternalHandler类中,在handleMessage中处理了得到的消息。这里的消息有两种类型MESSAGE_POST_RESULTMESSAGE_POST_PROGRESS
    MESSAGE_POST_RESULT时,执行了finish方法,代码为:

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

    在if语句中判断isCancelled的值,也就是任务是否取消来决定执行取消任务还是调用返回结果的方法onPostExecute。最后将任务状态设置为FINISHED
    MESSAGE_POST_PROGRESS时,执行了onProgressUpdate方法,用来更新任务执行的进度。

    • 2,3,4行:生成了一个WorkerRunnable对象mWorker,而这个类是个抽象类并且实现了Callable接口,因此mWorker也就是一个线程类。在call()方法中设置了线程的优先级,并调用了doInBackground(mParams)方法用来执行后台任务;如果出现异常就调用postResult(result)方法。
      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;
        }
    

    在这个方法中发送了一条消息类型为MESSAGE_POST_RESULT的消息。

    • 5,6,7行:将上面得到的mWorker作为参数生成了一个FutureTask对象mFuture,而FutureTask类实现了RunnableFuture接口,而RunnableFuture又实现了Runnable, Future接口,因此mFuture也可以看做是一个线程。接下来在FutureTask下的done方法中调用了postResultIfNotInvoked(get())方法。
      postResultIfNotInvoked(get())方法的具体代码如下:
        private void postResultIfNotInvoked(Result result) {
            final boolean wasTaskInvoked = mTaskInvoked.get();
            if (!wasTaskInvoked) {
                postResult(result);
            }
        }
    

    这个方法用来判定是否已经执行过postResult(result)方法。
    看第19行代码asyncTask.execute("AsyncTask 测试")中的代码实现:

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

    需要注意的是,这两个执行方法也是需要是主线程中执行的。执行代码最终进入了executeOnExecutor执行方法。此方法中现根据任务的mStatus状态值,判定任务是否正在执行或者已经执行过了,如果正在执行或者已经执行过了,就不能在调用执行方法进行执行了,也就是一个AsyncTask任务只能执行一次;如果没有执行过,将任务状态改为正在执行,然后调用onPreExecute()做执行任务前的准备工作,在将传入的参数赋值给mWorker中的mParams变量,最后线程池exec执行线程mFuture。这里的exec也就是sDefaultExecutor,定义如下:

        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();
                        } finally {
                            scheduleNext();
                        }
                    }
                });
                if (mActive == null) {
                    scheduleNext();
                }
            }
    
            protected synchronized void scheduleNext() {
                if ((mActive = mTasks.poll()) != null) {
                    THREAD_POOL_EXECUTOR.execute(mActive);
                }
            }
        }
    

    从这里的代码可以看出,SerialExecutor继承与Executor,重写了执行方法execute,并且给这个方法加了同步锁,而sDefaultExecutor是SerialExecutor的对象,所以这个线程池也就是一个串行的线程池,任务一个执行完成后另一个才能执行。方法execute中,将生成的新任务加入到mTasks任务队列尾部,而在新任务中执行了r.run()方法,此方法为调用exec.execute(mFuture)中传入的mFuture中的run方法,而在这个run方法中执行了mWorker中的call()方法,这样后台线程也就开始执行了。执行完r.run()之后,又执行了scheduleNext()方法。在这个方法中,通过调用mTasks.poll()方法,删除并获取队列中第一个元素的值,将其赋值mActive,如果值不为空,使用线程池THREAD_POOL_EXECUTOR执行线程mActive;为空则退出。

    这样AsyncTask的一整套流程就走完了,但是需要注意的是execute方法默认是串行执行的,也就是一个任务执行完了,另一个才能执行;如果你不想等待一个任务执行完成就去执行另一个任务,该怎么办呢?这时就需要调用executeOnExecutor方法,并传入系统为我们准备好的并行执行的线程池AsyncTask.THREAD_POOL_EXECUTOR,这样就完成了任务的并行执行。简单的例子如下:

            /**
             * 调用“execute”在程序还未执行完时,退出;那么子线程会在后台继续执行,直到执行完成;
             * 当子线程在没有执行完成时,在此调用“execute”方法,此时新建立的子线程并不会执行,只有等上一个子线程执行完成后才能执行。
             * 因为这个方法中的线程池“AsyncTask.SERIAL_EXECUTOR”是串行执行的。
             */
            task.execute("Use Test of AsyncTask.", "1000");
            /**
             * 调用“executeOnExecutor”在程序还未执行完时,退出;子线程在后台继续运行;
             * 当子线程在没有执行完成时,在此调用“executeOnExecutor”方法,新建立的子线程立刻执行。
             * 因为这个方法中的线程池“AsyncTask.THREAD_POOL_EXECUTOR”是并行执行的
             */
            task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "Use Test of AsyncTask.", "1000");
    

    在上面的代码中,也注释了使用并行的一种情况。

    相关文章

      网友评论

          本文标题:AsyncTask知识梳理

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