AsyncTask的原理解析

前言

AsyncTask是一个常用的已经封装好的异步任务类,可以更加方便地执行后台任务以及切换主线程去更新UI。从实现上来说,它封装了Thread(线程池)和Handler

定义

一个抽象的泛型类,提供了Params, Progress, Result三个泛型参数。

  • a. Params:开始异步任务执行时传入的参数类型,对应excute()中传递的参数
  • b. Progress:异步任务执行过程中执行进度值的类型
  • c. Result:异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型保持一致
public abstract class AsyncTask {
}

核心方法

结合源码的方式的方式看一下AsyncTask的一些核心方法

1.execute

手动调用开始执行异步任务,必须在UI线程中调用

   public final AsyncTask execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

然后内部调用了executeOnExecutor()方法

 public final AsyncTask 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;
    }

首先调用了onPreExecute()方法,然后调用了exec.execute(mFuture)方法开始执行,这里的exec是个串行的线程池sDefaultExecutor ,二话不说看定义。

 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
 public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
     private static class SerialExecutor implements Executor {
        final ArrayDeque mTasks = new ArrayDeque();
        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);
            }
        }
    }

AsyncTask中有两个线程池SerialExecutor(用于任务的排队)和THREAD_POOL_EXECUTOR(用于任务的执行)。

2.onPreExecute

回到onPreExecute方法,在主线程运行,异步任务运行之前执行,用于一些准备工作。可以选择性重写。

   @MainThread
    protected void onPreExecute() {
    }

下面看看AsyncTask的构造方法

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

        mWorker = new WorkerRunnable() {
            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(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);
                }
            }
        };
    }

构造方法中对前面executeOnExecutor方法中提到的mWorkermFuture进行了初始化操作,然后执行了doInBackground()方法,而后又执行了postResult(result)方法。接下来分别讲述这两个方法。

3.doInBackground

    @WorkerThread
    protected abstract Result doInBackground(Params... params);

抽象方法,必须重写自定义线程任务,接受Params参数,返回Result结果,可以在里面调用publishProgress()用于更新进度信息。

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

会利用Handler发个消息,MESSAGE_POST_PROGRESS标志更新UI,然后收到消息后会在主线程调用onProgressUpdate ()方法,完美实现了线程切换操作。

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

4.onProgressUpdate

在主线程中执行,后台进度发生改变时调用,可以选择重写。

    @MainThread
    protected void onProgressUpdate(Progress... values) {
    }

上述讲述了执行完doInBackground()方法,而后又执行了postResult(result)方法

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result));
        message.sendToTarget();
        return result;
    }

同样也是发送了消息,前面已经贴了Handler代码,这次会调用result.mTask.finish(result.mData[0])方法。继续跟

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

逻辑很简单,如果AsyncTask被取消执行了,就调用onCancelled(),否则调用onPostExecute(),将doInBackground()的返回结果传递给了onPostExecute()方法

5.onPostExecute

在主线程执行,返回doInBackground()的执行结果,将结果显示到UI。

    @MainThread
    protected void onPostExecute(Result result) {
    }

6.onCancelled()

在主线程执行,当异步任务被取消时,onCancelled()会被调用,这个时候onPostExecute不会调用。

    @MainThread
    protected void onCancelled() {
    }

重要类

前面多次提到了这两个类WorkerRunnableFutureTask

    private final WorkerRunnable mWorker;
    private final FutureTask mFuture;

1.WorkerRunnable

 private static abstract class WorkerRunnable implements Callable {
        Params[] mParams;
    }

这里的Callable也是任务, 与Runnable的区别是Callable存在返回值 。

2.FutureTask

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

其实就是一个任务包装类,里面包含Callable、增加了一些状态标识 ,操作Callable的接口。

3.THREAD_POOL_EXECUTOR

在前面提到SerialExecutorscheduleNext()方法中调用了THREAD_POOL_EXECUTOR.execute(mActive)

/**
  * 源码分析:THREAD_POOL_EXECUTOR.execute()
  * 说明:
  *     a. THREAD_POOL_EXECUTOR实际上是1个已配置好的可执行并行任务的线程池
  *     b. 调用THREAD_POOL_EXECUTOR.execute()实际上是调用线程池的execute()去执行具体耗时任务
  *     c. 而该耗时任务则是步骤2中初始化WorkerRunnable实例对象时复写的call()
  * 注:下面先看任务执行线程池的线程配置过程,看完后请回到步骤2中的源码分析call()
  */

    // 步骤1:参数设置
        //获得当前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 sPoolWorkQueue =
                new LinkedBlockingQueue(128);

    // 步骤2: 根据参数配置执行任务线程池,即 THREAD_POOL_EXECUTOR
    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;
    }

串行or并行

Android 1.6之前,AsyncTask是串行执行的,Android1.6之后采用线程池任务并行执行,从Android 3.0开始,AsyncTask又开始串行执行,就是一个接一个执行。当然也可以通过设置executeOnExecutor(Executor)来实现多个AsyncTask并行。

注意点

在使用AsyncTask时有一些问题需要注意的:

1 关于 生命周期

  • 结论
    AsyncTask不与任何组件绑定生命周期
  • 使用建议
    Activity 或 Fragment中使用 AsyncTask时,最好在Activity 或 FragmentonDestory()调用 cancel(boolean)

2 关于 内存泄漏

  • 结论
    AsyncTask被声明为Activity非静态内部类,当Activity需销毁时,会因AsyncTask保留对Activity的引用 而导致Activity无法被回收,最终引起内存泄露
  • 使用建议
    AsyncTask应被声明为Activity的静态内部类

3 线程任务执行结果 丢失

  • 结论
    Activity重新创建时(屏幕旋转 / Activity被意外销毁时后恢复),之前运行的AsyncTask(非静态的内部类)持有的之前Activity引用已无效,故复写的onPostExecute()将不生效,即无法更新UI操作
  • 使用建议
    Activity恢复时的对应方法 重启 任务线程

你可能感兴趣的:(AsyncTask的原理解析)