美文网首页
AsyncTask源码解析

AsyncTask源码解析

作者: lizb | 来源:发表于2019-01-10 17:36 被阅读0次

在Android开发中,相信有很多小伙伴都用过AsyncTask,我们在做轻量级的耗时任务并且需要更新UI时,需要将这些任务放到子线程里去执行,这时可以选择AsyncTask来满足我们的需求,这里我们就来分析一下它是如何实现的。由于AsyncTask在不同版本的实现是不同的,这里基于4.4以后版本的源码来分析。

先借用官方的例子:

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
      protected Long doInBackground(URL... urls) {
          int count = urls.length;
          long totalSize = 0;
          for (int i = 0; i < count; i++) {
              totalSize += Downloader.downloadFile(urls[i]);
              publishProgress((int) ((i / (float) count) * 100));
              // Escape early if cancel() is called
              if (isCancelled()) break;
          }
          return totalSize;
      }
 
     protected void onProgressUpdate(Integer... progress) {
          setProgressPercent(progress[0]);
      }
 
      protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
      }
 }

然后在使用的时候:

new DownloadFilesTask().execute(url1, url2, url3);

非常的简单,只需要写个类继承AsyncTask,复写需要的方法,传递相关参数即可。

首先,我们来看构造对象AsyncTask时做了哪些事:

    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) {

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

可以看到,默认构造函数里调用了this((Looper) null);调用了一个被隐藏的构造函数,传入的Looper为null,这个函数里,就只做了一件事情:初始化mHandler、mWork、mFuture这三个成员变量。至于怎么初始化的,我们暂且先搁置,先来看看这三个到底是何方神圣?

    private final Handler mHandler;
    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;
 private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
 }  
public interface Callable<V> {
    V call() throws Exception;
}
public class FutureTask<V> implements RunnableFuture<V> {

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
      }
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

Handler是我们熟悉的用于线程间通信的类。WorkerRunnable是Callable的一个抽象子类,里面只有一个Params[]型的变量,用于存储我们传入的参数,还有一个需要复写的call()方法,任务的执行就是在这个方法里面实现的。FutureTask是什么,我们可以看一下官方说明:

FutureTask can be used to wrap a Callable or Runnable object. Because FutureTask implements Runnable a FutureTask can be submitted to an Executor for execution.

简单的说,就是一个Callable 和 Runnable的包装类,是可以被提交到线程池Executor里直接执行的(调用其run方法)。

现在,我们回过头来看看对这三个变量的初始化过程:
由于传入的looper为null,所以mHandler=getMainHandler().

 private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
 }

可以看出mHandler指向的是一个InternalHandler的单例对象,请注意到构造InternalHandler对象时传入的是Looper.getMainLooper(),这个是主线程的Looper,所以通过此handler发送的消息会存储在主线程的消息队列中,并在主线程中被处理。来看看InternalHandler类的定义:

  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的子类,复写了handleMessage方法,在里面处理了两种任务:

  1. MESSAGE_POST_PROGRESS 对任务的过程更新处理
  2. MESSAGE_POST_RESULT 对任务的结果处理

上面的两种消息都会被mHandler发送到UI线程的消息队列中,并在UI现在中被处理,而对应的处理方法就是onProgressUpdate和onPostExecute。

再来看mWorker的初始化:

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

直接创建了一个WorkerRunnable的匿名子对象,实现了call() 方法,在该方法中,先将mTaskInvoked.set(true),标记该任务已被执行,接着设置线程的优先级,然后就是回调doInBackground方法,这个方法也就是需要我们自己实现的方法,在开篇官方的例子中,我们可以看到,在这个方法里面,通过不断调用publishProgress方法来对过程进度进行更新,在publishProgress方法里将进度值封装到AsyncTaskResult里,然后通过mHandler发送到UI线程的消息队列,交给我们上面分析的handleMessage里去处理,从而让UI线程去更新界面。

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

当doInBackground方法调用完成后,将返回的结果传入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;
    }

和publishProgress一样,将结果封装到AsyncTaskResult中,然后交给handler去处理,也就是在UI线程中调用我们复写的onPostExecute去处理结果。

最后来看看mFuture的初始化:

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

也是直接创建了一个FutureTask的匿名子对象,并传入了mWorker,之前我们说FutureTask是对Runnable的包装,所以这里就是对mWorker进行了包装,然后复写了done()方法,在其中调用了postResultIfNotInvoked(get());其中,get()方法获取的是任务完成的结果,也就是doInBackground返回的结果,来看下postResultIfNotInvoked方法的源码:

    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

首先,获取该任务的执行状态,如果已经执行了,就不执行postResult(result)方法,postResult方法我们刚才已经分析过了,至于这个任务的状态,前面我们在初始化mWorker的call方法中已经设置为true了,而后面经过我们分析可以知道mWorker的call方法先于mFuture的done()方法调用,所以,这里的postResult(result)方法基本执行不到。

到目前为止,我们对AsyncTask的构造方法分析完了。所有这些做的都是初始化内容,任务都还没有开始执行。接下来,我们分析任务的真正执行过程。

当我们调用new DownloadFilesTask().execute(url1, url2, url3)时,才能真正的执行任务,来看一下execute方法源码:

    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;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

execute方法里面调用了executeOnExecutor方法,在executeOnExecutor中,首先判断任务的状态,如果任务正在执行或是已经执行完成,则会抛出异常,然后回调我们自己复写的onPreExecute()方法做准备工作(UI线程),接着将我们传递进来的参数保存到mWorker对象的mParams变量中,最后调用exec.execute(mFuture)去执行任务,而这里传入的exec是sDefaultExecutor,因此我们来看一下sDefaultExecutor的初始化:


private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

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

sDefaultExecutor指向了一个SerialExecutor类型的对象,SerialExecutor是Executor的实现类,里面有一个成员变量mActive,指向当前正在执行的任务,同时维护着一个双向队列,存储的是等待执行的任务,可以看到,在execute方法中,先将我们传递进来的mFuture外面再包装一层,这里的包装很有意思,非常巧妙的实现了任务串行执行的效果,这应该是我们学习的地方;包装之后的Runnable对象被添加到这个任务队列中去,接下来,如果是首次执行,则调用scheduleNext()方法,在该方法里,首先从队列中拿出一个任务,并赋值给mActive,如果不为null,则交给线程池THREAD_POOL_EXECUTOR去真正的执行任务,线程池在执行任务的过程中,会先调用包装过的Runnable对象的run方法,在run方法中又调用mFuture的run方法,就像剥壳一样,一层一层的拆开,mFuture的run方法调用后,一个任务就真正的执行完成了,然后继续调用scheduleNext()从mTasks任务队列中获取下一个任务,并继续交给线程池去执行,如此循环。由此可以看出整个任务队列的执行是串行的。

现在我们来看mFuture的run方法中任务的执行过程:

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就是在构造mFuture时传入的mWorker对象,
可以看出如果mWorker不为null,就会回调mWorker的call方法,
而call方法,我们之前就已经分析过了,里面就是任务的真正执行过程,执行完成之后,将变量ran=true,如果没有异常,就执行set(result)方法,最后在finally里做一些收尾的工作。
来看下set(result)的源码:

protected void set(V v) {
        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
            outcome = v;
            U.putOrderedInt(this, STATE, NORMAL); // final state
            finishCompletion();
        }
    }

将outcome指向任务返回的结果,然后调用finishCompletion():

 private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (U.compareAndSwapObject(this, WAITERS, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }

        done();

        callable = null;        // to reduce footprint
    }

这个方法里移除所有等待的线程,然后调用了done()方法,最后将callable置空,断开与mWorker的关联。由此也验证了我们之前的mWorker的call方法先于mFuture 的 done()执行。

好了,AsyncTask的源码就分析到这里,接下来,总结一下AsyncTask的优劣性:

1.这里是基于4.4及以上的版本,在3.0之前的版本中,任务是并行执行的,在3.0以上的版本中,任务都是串行执行的,4.4和3.0-4.4之间的版本区别不大,只是将线程池数量改为了动态的了。所有,开发的时候可能需要对版本做出判断,对于不同版本做一些不同的处理。

2.在Activity中定义AsyncTask导致内存泄漏,由于AsyncTask是Activity的内部类,所以会持有外部的一个引用,如果Activity已经退出,但是AsyncTask还没有执行完毕,那么Activity就无法释放导致内存泄漏。对于这个问题我们可以把AsyncTask定义为静态内部类并且采用弱引用。

3.通过以上分析,我们知道了再3.0以后的手机中AsyncTask时串行执行的,如果有一个任务执行时间过长,会导致后面的任务只能在队列中等待,导致阻塞,所以可以考虑把执行时间较短的任务优先加入。如果有需要我们可以直接使用executeOnExecutor方法,然后直接使用THREAD_POOL_EXECUTOR线程池来执行,这样可以多个线程同时执行,这样可以提高效率。

相关文章

  • Android AsyncTask 源码解析

    标签:Android AsyncTask 源码解析 1.关于AsyncTask 1.1 什么是AsyncTask?...

  • 4.AsyncTask使用,原理

    资料 AsyncTask处理机制详解及源码分析-工匠若水 AsyncTask 源码解析-鸿洋 带你认识不一样的As...

  • Android日记之AsyncTask源码解析

    前言 AsyncTask的使用方法请看Android日记之AsyncTask的基本使用,此篇的源码解析我们还是从使...

  • AsyncTask原理解析

    AsyncTask是一个串行的线程,本文主要通过源码解析它的原理 -->从 AsyncTask执行的方法execu...

  • AsyncTask 源码解析

    一、前言AsyncTask是一个异步任务。里面封装了线程池及Handler。所以,它可以方便地实现线程的切换及耗时...

  • AsyncTask源码解析

    参考资料:Android开发艺术探索 AsyncTask是一个Android官方提供的一种轻量级的异步任务类,它可...

  • AsyncTask源码解析

    AsyncTask 执行轻量级的异步任务,将结果传递给主线程,主线程根据结果更新UI. 使用 AsyncTask创...

  • AsyncTask源码解析

    AsyncTask源码解析 最近再刷一些基础的东西,所以就随便记录了一些看源码的心得,目前开发中见到了很多Asyn...

  • AsyncTask源码解析

    参考资料 鸿洋版AsyncTask郭霖版AsyncTask线程池Android开发艺术探索Android源码 相关...

  • AsyncTask源码解析

    变量 1. CPU_COUNT CPU总数 2. CORE_POOL_SIZE 核心线程数 3.MAXIMUM...

网友评论

      本文标题:AsyncTask源码解析

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