zoukankan      html  css  js  c++  java
  • 带你轻松看源代码---AsyncTask(异步任务)

    本文出自博客Vander丶CSDN博客,如需转载请标明出处,尊重原创谢谢

    博客地址:http://blog.csdn.net/l540675759/article/details/62893318

    写作背景

    • 愚人节特别篇

    • 这篇博客准备了好久,一直放在草稿箱里面,随着之前的深入了解Android线程池 . 完结之后,在写这篇文章就顺手了非常多.

    • 写这篇博客的时候,仅仅想了解下AsyncTask,没想到看着源代码之后就沉迷了,一点一点看了队列,同步,异步,也尝试着翻译英文文档,总之收货满满.

    • 本文用图,所有为本人独立创作,假设转载请注明出处,请尊重原创谢谢.

    • 本文大部分观点,属于博主个人观点,如有错误,希望大家多多指正,然后博主及时更改.

    • 最后希望看到博主文章的小伙伴,能够给博主顶一顶博客,欢迎留言,欢迎讨论.

    前言:

    • 本篇文章主要介绍AsyncTask的内部工作原理

    • 在阅读文本之前,假设你不太了解AsyncTask,能够先參考我的博客AsyncTask(异步任务)分析之基本使用

    • 文中涉及到一些术语如:串行、并行、队列这些关键词会在导读中为大家介绍


    导读:

    AsyncTask的组成非常easy。可是还有有一些关键的知识点,导读中将会把一些比較重要的概念整理出来。让大家提前了解下,会使接下来的阅读更顺畅。

    串行和并行的概念

    在Android3.0之前,AsyncTask一直是并行运行的。而在Android3.0之后更改为串行运行,假设对串行和并行概念不太了解。能够參阅下我之前的线程中同步、异步、串行、并行这篇文章,通过图能让你迅速了解串行和并行的基本概念。


    Android的消息机制,Handler的原理

    在AsyncTask的工作原理中,InternalHandler是其运转的主要一环。AsyncTask通过它,来保持和UI线程的通信。从而实现进度的更新,以及AsyncTask的回调、中断的通知。
    假设对Handler机制不太熟悉。能够參考下android的消息机制——Handler机制这篇文章。


    队列的概念。容器ArrayDeque的使用

    在AsyncTask中,有一个双端队列的线程池。通过其来保持AsyncTask的调度,而其内部的容器就是ArrayDeque。对于ArrayDeque网上的介绍比較少,这里能够參考我整理的深入了解双端队列Deque能够大致了解ArrayDeque的结构。


    Android中的线程池

    AsyncTask中採用线程池用于任务的调度和运行,假设对线程池不太了解,能够參考我的博文深入了解Android线程池 .


    AsyncTask的源代码解析

    AsyncTask的主要成员

    从AsyncTask的内部模块图能够看出,AsyncTask主要分为下面几个模块 :

    (1)用于任务的调度的线程池SerialExecutor,图中的任务队列池.
    (2)用于任务的运行的线程池ThreadPoolExecutor,图中的运行任务的线程池.
    (3)用于和UI线程交互的InternalHandler,通知任务的完毕的进度,以及任务的一些状态.

    AsyncTask的状态

        public enum Status {
            /**
             * AsyncTask的初始状态,表明AsyncTask处于未运行任务的状态
             */
            PENDING,
            /**
             * AsyncTask的运行状态,表明AsyncTask正在运行任务,正在运行
             */
            RUNNING,
            /**
             * AsycnTask的完毕状态,表明AsyncTask处于完毕状态,而且会调用onPostExecute
             */
            FINISHED,
        }

    任务调度线程池SerialExecutor

    我们能够看到SerialExecutor仅仅是实现了Executor这个接口,其内部维持的队列是ArrayDeque(一种双向队列).它会在一个任务运行结束,和SerialExecutor初始化之后,会自己主动去队列中获取任务并通过THREAD_POOL_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);
                }
            }
        }

    任务的运行的线程池THREAD_POOL_EXECUTOR

    运行线程池的初始化过程:

        //获得当前CPU的核心数
        private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        //设置线程池的核心线程数2-4之间,可是取决于CPU核数
        private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
        //设置线程池的最大线程数为 CPU核数*2+1
        private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
        //设置线程池空暇线程存活时间30s
        private static final int KEEP_ALIVE_SECONDS = 30;
    
        //初始化线程工厂
        private static final ThreadFactory sThreadFactory = new ThreadFactory() {
            private final AtomicInteger mCount = new AtomicInteger(1);
    
            public Thread newThread(Runnable r) {
                return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
            }
        };
    
        //初始化存储任务的队列为LinkedBlockingQueue 最大容量为128
        private static final BlockingQueue<Runnable> sPoolWorkQueue =
                new LinkedBlockingQueue<Runnable>(128);
    
        /**
         * 一个能够运行并行任务的线程池哦
         */
        public static final Executor THREAD_POOL_EXECUTOR;
    
        static {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                    sPoolWorkQueue, sThreadFactory);
            //设置核心线程池的 超时时间也为30s
            threadPoolExecutor.allowCoreThreadTimeOut(true);
            THREAD_POOL_EXECUTOR = threadPoolExecutor;
        }
    

    用于和UI交互的InternalHandler

        private static class InternalHandler extends Handler {
    
            //注意:这里是获取主线程的Looper(),也就是为什么AsyncTask能和主线程交互的原因
            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: //这里将结果通过Handler传递到主线程 result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: //这是是通知主线程更新进度的操作. result.mTask.onProgressUpdate(result.mData); break; } } }


    AsyncTask的内部模块图

    AsyncTask的内部模块图

    上图是AsyncTask的内部模块图。通过此图大家能够了解AsyncTask简单的工作原理。


    AsyncTask的构造方法

    public AsyncTask() {
            mWorker = new WorkerRunnable<Params, Result>() {
                public Result call() throws Exception {
                    //加入线程的调用标识
                    mTaskInvoked.set(true);
                    Result result = null;
                    try {
                        //设置线程的优先级
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        //运行异步操作
                        result = doInBackground(mParams);
                        //将进程中未运行的命令,一并送往CPU处理
                        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 {
                        //在运行完任务做一道检查,将没被调用的Result也一并发出.
                        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) {
                        //假设发生异常,则将结果滞null发出.
                        postResultIfNotInvoked(null);
                    }
                }
            };
        }

    在AsyncTask的构造方法中,将任务包装好,然后进行初始化操作:

    在构造方法中,WorkerRunnable就一个能储存參数的Callable.

    //这里的Callable也是任务,可是与Runnable不同的是,Callable<T>存在返回值,返回值就为其泛型
        private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
            Params[] mParams;
        }
    

    而FutureTask也是一个包装类,我们来看下FutureTask的构造方法:

    //事实上FutureTask内部包括Callable<T>,而且添加了一些状态标识和暴漏出操作Callable<T>的一些接口.
        public FutureTask(Callable<V> callable) {
            if (callable == null)
                throw new NullPointerException();
            this.callable = callable;
            this.state = NEW;      
        }
    

    而其done()方法,就是FutureTask内的Callable运行完毕之后的调用方法.在done()方法中,对任务的调用进行复查,将未被调用的任务的结果通过InternalHandler传递到UI线程.

    //方法非常easy,就是取得标志,然后推断该标志而已,复查没有被调用的任务,将其Result对象发送出去.
        private void postResultIfNotInvoked()(Result result) {
            final boolean wasTaskInvoked = mTaskInvoked.get();
            //假设没有被运行,也须要把结果发送出去.
            if (!wasTaskInvoked) {
                postResult(result);
            }
        }

    AsyncTask的工作原理

    通过上面的介绍,我们了解了AsyncTask的核心组成,以及AsyncTask在初始化所做的操作.

    execute()

    那么接下来,我们来一起看下AsyncTask的工作原理,这里我们从AsyncTask的运行方法execute()開始:

        @MainThread
        public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        //从这里我们发现终于调用的方法是executeOnExecutor()方法
        //此时是通过队列线程池储存任务,然后运行线程池取出任务运行.
            return executeOnExecutor(sDefaultExecutor, params);
        }

    executeOnExecutor()

       @MainThread
        public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                Params... params) {
                //推断AsyncTask当前的运行状态,PENDING为初始化状态
            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)");
                }
            }
            //executeOnExecutor()被调用时,AsyncTask的状态就变成了RUNNING状态
            mStatus = Status.RUNNING;
            //此时进行准备工作(主线程)
            onPreExecute();
            //将參数加入到任务中
            mWorker.mParams = params;
            //运行任务
            exec.execute(mFuture);
            return this;
        }

    AsyncTask的基本运行流程大致的情况,上面已经介绍,须要注意的是,在Android3.0之前AsyncTask是并行运行的.

    而在Android3.0之后默认AsyncTask是串行运行的.关于线程的串行和并行这里假设不明确,能够參考导读.详细大家能够在Android模拟器分别在3.0下面和3.0以上的版本号进行測试.

    而假设在Android3.0以上的版本号并行运行AsyncTask,我们能够这样:

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            Log.d(TAG, "异步任务完毕阶段阶段");
            SimpleDateFormat df =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Log.d(TAG, "当前异步任务 : "+"	"+ this.hashCode()+"" );
            Log.d(TAG, "当前结束时间 : "+"	"+df.format(new Date()) );
        }
    //这里直接调用executeOnExecutor()方法,而且制定处理的线程池为 //AsyncTask.THREAD_POOL_EXECUTOR,也就是运行线程池
    new 
    TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
    new TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
    new TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
    new TestAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");

    通过在完毕方法打印当前的AsyncTask的hashCode来区分是不是同一个AsyncTask,以及时间上的差异来确定是否是并行,得到的结果为:

    03-31 13:13:48.505 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前异步任务 :  632327564
    03-31 13:13:48.505 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前结束时间 :  2017-03-31 13:13:48
    03-31 13:13:48.505 7050-7050/com.commonproject.debug D/TestAsyncTask: 异步任务完毕阶段阶段
    03-31 13:13:48.506 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前异步任务 :  496311509
    03-31 13:13:48.506 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前结束时间 :  2017-03-31 13:13:48
    03-31 13:13:48.507 7050-7050/com.commonproject.debug D/TestAsyncTask: 异步任务完毕阶段阶段
    03-31 13:13:48.507 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前异步任务 :  688151786
    03-31 13:13:48.507 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前结束时间 :  2017-03-31 13:13:48
    03-31 13:13:48.508 7050-7050/com.commonproject.debug D/TestAsyncTask: 异步任务完毕阶段阶段
    03-31 13:13:48.508 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前异步任务 :  304548827
    03-31 13:13:48.508 7050-7050/com.commonproject.debug D/TestAsyncTask: 当前结束时间 :  2017-03-31 13:13:48

    由上述结果能够发现,上述的并行的运行过程直接绕过了队列线程池,直接制定运行线程池去运行任务.那么并行的原因在哪呢 ?

    public AsyncTask() {
                        ......
                    try {
                        //设置线程的优先级
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        //运行异步操作
                        result = doInBackground(mParams);
                        //将进程中未运行的命令,一并送往CPU处理
                        Binder.flushPendingCommands();
                        .........

    注意Binder.flushPendingCommands()这个JNI命令.

        /**
         * Flush any Binder commands pending in the current thread to the kernel
         * driver.  This can be
         * useful to call before performing an operation that may block for a long
         * time, to ensure that any pending object references have been released
         * in order to prevent the process from holding on to objects longer than
         * it needs to.
         */
        public static final native void flushPendingCommands();

    这种方法我的理解是将进程中等待的Binder命令,一并提交给CPU处理,当然这会造成一些堵塞,我们知道线程的运行也是依靠CPU来运转,所以我觉得这种方法才是AsyncTask能够同步运行的关键.

    在每一个任务运行完时,都会把当前进程的等待任务提交,然后堵塞,等都完毕,该进程内没有Thread提交时,一起返回,从而形成同步.

    而在这里之所以不去使用队列线程池的原因也在这,由于队列线程池实现了锁的机制,而且通过它的代码我们能够得知它是处理完一个任务,才会去下个任务,这一块也是AsyncTask默认能够实现串行的原因.

    这里写图片描写叙述


    postResult()方法

        private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            //正常的把结果通过InternalHandler发送给UI线程.
            Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                    new AsyncTaskResult<Result>(this, result));
            message.sendToTarget();
            return result;
        }

    这种方法,没有太多可介绍的.熟悉Handler机制的,自然就能看懂.当postResult()方法运行后,InternalHandler
    会调用AsyncTask的finish()方法.


    finish()方法

        private void finish(Result result) {
            //推断任务是否被取消,假设被取消则回调onCalled()
            if (isCancelled()) {
                onCancelled(result);
            } else {
                //请求成功
                onPostExecute(result);
            }
            //变更AsyncTask的状态
            mStatus = Status.FINISHED;
        }

    finish()是返回结果的方法,无论AsyncTask是否被取消都会将该AsyncTask的状态变更成FINISHED.


    总结

    最后基本上整个AsyncTask的介绍也就清晰了,本文的介绍的思路是:

    1.先介绍AsyncTask的核心构成.
    2.在介绍AsyncTask的工作流程:
     execute() -->executeOnExecutor() -->postResult() --> finish() 
    3.还了解了AsyncTask中的串行和并行的特点,以及AsyncTask实现并行和串行的原理.

    AsyncTask内部工作流程图解

    AsyncTask的内部工作逻辑

    这图可能画的有点复杂,事实上细致对比上文的逻辑和源代码看,你一定会弄明确的.而且通读全然文之后,你会发现AsyncTask并不难,原来是这么简单.


    參考文章

    1.安卓艺术探索
  • 相关阅读:
    将代码托管到github服务器之SSH验证
    将代码托管到github服务器之HTTPS验证
    git的基本介绍和使用
    iOS之UITableView组头组尾视图/标题悬停
    iOS事件传递->处理->响应
    NSRunLoop
    Podfile使用说明
    cocoapods安装
    block
    自定义UIBarButtonItem
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/7260514.html
Copyright © 2011-2022 走看看