zoukankan      html  css  js  c++  java
  • AsyncTask源码解析

    本文分为以下几部分:
    1.AsyncTask的使用介绍
    2.AsyncTask的实现逻辑
    3.其它牵涉到的概念
     
    1.AsyncTask的使用
    class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
        @Override
        protected void onPreExecute() {
            progressDialog.show();
        }
        @Override
        protected Boolean doInBackground(Void... params) {
            try {
                while (true) {
                    int downloadPercent = doDownload();
                    publishProgress(downloadPercent);
                    if (downloadPercent >= 100) {
                        break;
                    }
                }
            } catch (Exception e) {
                return false;
            }
            return true;
        }
        @Override
        protected void onProgressUpdate(Integer... values) {
            progressDialog.setMessage("当前下载进度:" + values[0] + "%");
        }
        @Override
        protected void onPostExecute(Boolean result) {
            progressDialog.dismiss();
            if (result) {
                Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
            }
        }
    }
    View Code
    class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
        ……
    }
    View Code

    这里我们把AsyncTask的第一个泛型参数指定为Void(doInBackground(Params...)),表示在执行AsyncTask的时候不需要传入参数给后台任务。第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位(onProgressUpdate(Progress...))。第三个泛型参数指定为Boolean(onPostExecute(Result)),则表示使用布尔型数据来反馈执行结果。

    1. onPreExecute()
    这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
    2. doInBackground(Params...)---只有在这个方法中执行耗时操作
    这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
    3. onProgressUpdate(Progress...)
    当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
    4. onPostExecute(Result)
    当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
     
    执行:执行如下代码,就开启一个下载任务(实际可能只是放到队列中,没有开始执行)
    new DownloadTask().execute();
    View Code
    需要传递参数给后台任务,那么如何定义?
    这样定义:
    class DownloadTask extends AsyncTask<String, Integer, Boolean>{
    
    @Override
        protected Boolean doInBackground(String... params) {
        ....
    // String url = params[0]; ,这个就是客户端传入的参数
    // String url2 = params[1]; 客户端传入的参数
       }
    }
    //使用
    DownloadTask  task = new DownloadTask();
    task.execute("url","url2");
    View Code

    2.AsyncTask的实现逻辑

    1).AsyncTask是一个抽象类,子类继承抽象类,要实现抽象类的抽象方法
    2).要实现的抽象方法:
    抽象类AsyncTask只有一个抽象方法doInBackground,子类是必须要实现的
        /**
         * Override this method to perform a computation on a background thread. The
         * specified parameters are the parameters passed to {@link #execute}
         * by the caller of this task.
         *
         * This method can call {@link #publishProgress} to publish updates
         * on the UI thread.
         *
         * @param params The parameters of the task.
         *
         * @return A result, defined by the subclass of this task.
         *
         * @see #onPreExecute()
         * @see #onPostExecute
         * @see #publishProgress
         */
        @WorkerThread
        protected abstract Result doInBackground(Params... params);
    View Code
    3).创建子类的一个实例,然后执行execute方法,这样就开启一个AsyncTask任务。
    那么,这个时候,要查看创建一个实例,然后执行execute方法,都做了什么。
    实例化AsyncTask时做的事情:
      /**
         * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
         */
        public AsyncTask() {
            mWorker = new WorkerRunnable<Params, Result>() {
                public Result call() throws Exception {
                    mTaskInvoked.set(true);
    
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    Result result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                    return postResult(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 abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
            Params[] mParams;
        }
    View Code

    调用execute方法做的事情:

      /**
         * Executes the task with the specified parameters. The task returns
         * itself (this) so that the caller can keep a reference to it.
         *
         * <p>Note: this function schedules the task on a queue for a single background
         * thread or pool of threads depending on the platform version.  When first
         * introduced, AsyncTasks were executed serially on a single background thread.
         * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
         * to a pool of threads allowing multiple tasks to operate in parallel. Starting
         * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being
         * executed on a single thread to avoid common application errors caused
         * by parallel execution.  If you truly want parallel execution, you can use
         * the {@link #executeOnExecutor} version of this method
         * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings
         * on its use.
         *
         * <p>This method must be invoked on the UI thread.
         *
         * @param params The parameters of the task.
         *
         * @return This instance of AsyncTask.
         *
         * @throws IllegalStateException If {@link #getStatus()} returns either
         *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
         *
         * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
         * @see #execute(Runnable)
         */
        @MainThread
        public final AsyncTask<Params, Progress, Result> execute(Params... params) {
            return executeOnExecutor(sDefaultExecutor, params);
        }
    
        /**
         * An {@link Executor} that can be used to execute tasks in parallel.
         */
        public static final Executor THREAD_POOL_EXECUTOR
                = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                        TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
    View Code
    似乎,阅读完注释,就知道它的执行原理了。
     
    执行execute,就是创建一个任务,把这个任务加入到一个队列中,然后等待线程(可以是单一的线程,也可以是从线程池中取出来的线程)来执行。因为,所有的实例都是使用同一个THREAD_POOL_EXECUTOR(是一个静态final变量,当某个类首次加载的时候,就会初始化,之后就是供所有的实例使用)。那么,每当我们new DownloadTask().execute("url"),创建一个任务,然后将任务推给队列,这个执行的顺序是怎样的呢?
    在早期的系统版本(低于android.os.Build.VEERSION_CODES#DONUT的版本),是采用串行的方式执行,某一个时刻只有一个任务在执行;从系统版本android.os.Build.VERSION_CODES#DONUT开始,修改为并行的方式执行,多个任务,分配给多个线程,并行执行;到了版本android.os.Build.VERSION_CODES#HONEYCOMB,它又修改为串行的方式。真是折腾。
    并行执行的问题,看应用场景,如果某些任务是要按顺序一个一个执行的,对执行顺序有要求的,那么这些任务就不能采用并行的方式执行,而要采用串行的方式执行。因为并行执行,它不能确保所有任务的执行顺序。
     
    现在默认是串行的方式,如果需要该任务是并行执行的,那么,使用如下方式:
    DownloadTask task = new DownloadTask();
    task.executeOnExecutor(THREAD_POOL_EXECUTOR,"url");
    View Code

    4)executeOnExecutor(Executor exec,Params... params)执行细节

      private final FutureTask<Result> mFuture;
    
        /**
         * An {@link Executor} that can be used to execute tasks in parallel.
         */
        public static final Executor THREAD_POOL_EXECUTOR
                = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                        TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
    
       /**
         * An {@link Executor} that executes tasks one at a time in serial
         * order.  This serialization is global to a particular process.
         */
        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);
                }
            }
        }
    
       /**
         * Executes the task with the specified parameters. The task returns
         * itself (this) so that the caller can keep a reference to it.
         *
         * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
         * allow multiple tasks to run in parallel on a pool of threads managed by
         * AsyncTask, however you can also use your own {@link Executor} for custom
         * behavior.
         *
         * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
         * a thread pool is generally <em>not</em> what one wants, because the order
         * of their operation is not defined.  For example, if these tasks are used
         * to modify any state in common (such as writing a file due to a button click),
         * there are no guarantees on the order of the modifications.
         * Without careful work it is possible in rare cases for the newer version
         * of the data to be over-written by an older one, leading to obscure data
         * loss and stability issues.  Such changes are best
         * executed in serial; to guarantee such work is serialized regardless of
         * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
         *
         * <p>This method must be invoked on the UI thread.
         *
         * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
         *              convenient process-wide thread pool for tasks that are loosely coupled.
         * @param params The parameters of the task.
         *
         * @return This instance of AsyncTask.
         *
         * @throws IllegalStateException If {@link #getStatus()} returns either
         *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
         *
         * @see #execute(Object[])
         */
        @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;
        }
    View Code
    a.SerialExecutor的实现逻辑:
    1).mTasks.offer(Runnable),创建一个新的Runnable,添加到队列尾部。这个新的Runnable就是交给THREAD_POOL_EXECUTOR执行的任务。新的Runnable包含了从客户端提交的任务。
    新的Runnable的逻辑是:先执行客户端提交的任务(也就是mFuture);然后,执行scheduleNext方法,scheduleNext做的事情,从队列的头部取出创建的Runnable,然后交给THREAD_POOL_EXECUTOR执行。
    通过这种方式,就可以保证所有的任务是按照串行的方式来执行的。新添加的mFuture,使用新创建的一个Runnable,然后,将这个Runnable添加到队列尾部;然后,每次执行的时候,是从队列的的头部取添加的Runnable。
    2).触发执行的入口是:
       if (mActive == null) {
             scheduleNext();
     }
    3).scheduleNext方法,是同步方法,确保每次只有一个线程可以执行该方法。THREAD_POOL_EXECUTOR有多个线程。
      protected synchronized void scheduleNext() {
                if ((mActive = mTasks.poll()) != null) {
                    THREAD_POOL_EXECUTOR.execute(mActive);
                }
            
    View Code

    通过SerialExecutor,就可以实现串行的执行。

    实现逻辑总结:
    1.执行从在executeOnExecutor开始,在该方法中调用 onPreExecute(),这是在UI线程执行的。
    2.当THREAD_POOL_EXECUTOR有空闲线程时,就会最终执行mFuture任务,而mFuture包含mWorker,它会执行:Result result = doInBackground(mParams);----mFuture是在第三方线程中运行的
    3.将执行的结果发送给UI线程:
     在mWorker中会调用postResult方法;postResult方法,通过UI线程的handler发送消息。这里的关键是InternalHandler的创建,它使用的是Ui线程的Looper,这样消息才会给Ui线程的消息循环处理。
      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 Handler getHandler() {
            synchronized (AsyncTask.class) {
                if (sHandler == null) {
                    sHandler = new InternalHandler();
                }
                return sHandler;
            }
        }
    
        private static class InternalHandler extends Handler {
            public InternalHandler() {
                super(Looper.getMainLooper());
            }
    
            @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;
                }
            }
        }
    View Code
    4.执行过程中的进度更新:
    因为该方法是在第三方线程中运行的,所以,同样是使用Ui线程的Handler来发送消息
    /**
         * This method can be invoked from {@link #doInBackground} to
         * publish updates on the UI thread while the background computation is
         * still running. Each call to this method will trigger the execution of
         * {@link #onProgressUpdate} on the UI thread.
         *
         * {@link #onProgressUpdate} will not be called if the task has been
         * canceled.
         *
         * @param values The progress values to update the UI with.
         *
         * @see #onProgressUpdate
         * @see #doInBackground
         */
        @WorkerThread
        protected final void publishProgress(Progress... values) {
            if (!isCancelled()) {
                getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                        new AsyncTaskResult<Progress>(this, values)).sendToTarget();
            }
        }
    View Code
    3.其它牵涉到的概念介绍
     
    1.FutureTask
    2.Callable
     
     
     
    引用:
    部分示例代码引用自网络
     
     
  • 相关阅读:
    Python:完全数
    Python:将 list 写入一个 txt 文件
    Python:对称数组
    Python:列表反转、切片
    Python:print输出间隔,换行
    Python:打印99乘法表
    Python:排序(sort / 冒泡排序)
    安装pipenv
    flex布局
    python正则表达式
  • 原文地址:https://www.cnblogs.com/ttylinux/p/7182131.html
Copyright © 2011-2022 走看看