美文网首页
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线程池来执行,这样可以多个线程同时执行,这样可以提高效率。

    相关文章

      网友评论

          本文标题:AsyncTask源码解析

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