美文网首页Android 进阶之旅
Android 进阶学习(十一) AsyncTask 源码学习

Android 进阶学习(十一) AsyncTask 源码学习

作者: Tsm_2020 | 来源:发表于2020-11-25 16:36 被阅读0次

AsyncTask 作为一个轻量级的异步操作类,他的封装还是非常具有代表性的,这篇文章我们通过源码来看一下他是如何实现的,

先分别介绍一下AsyncTask 中的角色

1.SerialExecutor 线程池 保存着需要被执行的任务队列,添加任务时,如果当前没有任务在执行,则调用任务线程池,执行任务,在任务执行结束后,也是判断任务队列中是否还有任务,有任务则调用线程池继续执行任务

2.THREAD_POOL_EXECUTOR 执行任务的任务队列,

3.InternalHandler 消息的传递着,用来发送消息到监听的线程, 这里不一定是主线程,

4.WorkerRunnable 真正工作的Runnable,

5.FutureTask 代表着需要被执行的任务,

再来说一下在AsyncTask 非常重要的方法

onPreExecute 当任务开始之前调用,用于准备数据,

doInBackground 耗时任务执行的方法,该方法工作在子线程,

onProgressUpdate 当进度发生变化后调用,作用于目标线程,通常为主线程

onPostExecute 当任务执行完毕后调用,

如何使用AsyncTask

      new AsyncTask<String ,Integer,String>(){

           @Override
           protected void onProgressUpdate(Integer... values) {
               super.onProgressUpdate(values);
           }

           @Override
           protected void onPostExecute(String s) {
               super.onPostExecute(s);
           }

           @Override
           protected String doInBackground(String... strings) {
               return null;
           }
       }.execute("");

AsyncTask<Params,Progress,Result> 我们可以看到构造AsyncTask的时候必须指定三个类型,Params就是在执行execute时的入参类型,这个根据自身来定义,Progress 就是进度类型,通常是Integer,第三个参数就是我们在执行完任务后返回的结果类型,

AsyncTask 构造方法

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

我们可以看到在构造方法中需要一个looper , 这个looper 决定着我们需要将消息最终传递到哪个线程,我们前面介绍InternalHandler 的时候就说了,这个消息的去处不一定时主线程,原因就是这个loopter,
我们再来看看这个InternalHandler

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

InternalHandler 是AsyncTask 的静态内部类,继承自handler , 他只能处理2种消息,一种是result 结果,另外一种是progress进度,这里我们看到一个上面介绍比价重要的方法onProgressUpdate,可以看到消息是通过InternalHandler来发送的,同样的AsyncTask 的finish 方法也是在这里处理的, 接收的类型也比较特别 AsyncTaskResult ,我们来看看他真面目

   private static class AsyncTaskResult<Data> {
       final AsyncTask mTask;
       final Data[] mData;

       AsyncTaskResult(AsyncTask task, Data... data) {
           mTask = task;
           mData = data;
       }
   }

AsyncTaskResult的构造方法必须有AsyncTask,这个Data 是根据当前的消息类型来决定的(进度or结果),也就是我们在创建AsyncTask时约定的类型,

继续来分析WorkerRunnable

WorkerRunnable

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
       Params[] mParams;
   }

WorkerRunnable 是一个抽象类, 实现了Callable, 并且非常的简单 只有一个mParams属性,
再回过头看看AsyncTask构造方法中WorkerRunnable 的创建

       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);
                   Binder.flushPendingCommands();
               } catch (Throwable tr) {
                   mCancelled.set(true);
                   throw tr;
               } finally {
                   postResult(result);
               }
               return result;
           }
       }

WorkerRunnable 中的 call 方法执行了一个我们在上面提到过的非常重要的方法 doInBackground , 我们看到在finally 方法中调用了postResult 方法

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

这里将result 和 当前的AsyncTask 封装成了一个类型是Result 的Msg, 并通过 InternalHandler 将它发送给目标线程

我们继续分析一下FutureTask

FutureTask

FutureTask 是一个接口,继承自 Runnable, Future,通过类的介绍我们可以将它定义为一个可以被取消的异步工作类,既然他可以被取消,那就代表着我们的耗时任务如果被添加到处理任务的队列中,我们也可以将它打断,我们先来看一下AsyncTask 中的取消方法,

public final boolean cancel(boolean mayInterruptIfRunning) {
       mCancelled.set(true);
       return mFuture.cancel(mayInterruptIfRunning);
   }

可以看到取消任务就是调用FutureTask 的cancel方法,具体怎么实现的取消的在这里就不多说了,大家有兴趣的可以去看一下源码,
继续来分析构造方法中初始化FutureTask 的代码

      mFuture = new FutureTask<Result>(mWorker) {
           @Override
           protected void done() {
     
           }
       };

可以看到一个done方法,那么这个done 方法的执行时机是在什么时候呢, FutureTask的入参是我们上面实现的继承自Callable 的WorkRunnable,

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

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

这FutureTask 的run方法中会调用我们workRunnable 的call 方法 ,然后执行 workRunnable 的doInBackground 的方法执行耗时任务,执行结束后将ran标记为true, 那么就会走下面那个set(Result)的方法,

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

检查了一下状态,继续执行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 方法,
再来分析一下上面初始化FutureTask 的done方法

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

在done 方法中可以到在这里会执行 postResultIfNotInvoked方法根据一个标识符来判断是否需要发送结果

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

那么这个标识符是在 workRunnable 的call方法中设置为true的,也就是说,如果执行了workRunnable 的call 方法,这里是肯定不会再次发送的消息

既然是done方法执行在workRunnable方法之后,那么为什么我们已经在workRunnable 的call方法中调用了postResult 的方法,这里还要再次判断呢,这就是我们前面介绍FutureTask时说他是可以被打断的,如果我们执行了 AsyncTask 的cancel 从而将 FuturnTask 打断,同样会执行这个done,而WorkRunnable 的call方法不会执行,此时就会发送结果

构造方法到这里已经分析完了,我们继续分析execute 方法,看看整个任务的执行过程,

execute

   @MainThread
   public final AsyncTask<Params, Progress, Result> execute(Params... params) {
       return executeOnExecutor(sDefaultExecutor, params);
   }

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

execute 方法的入参类型即是我们在创建AsyncTask 时定义的的Params类型,在调用execute 方法后,就会调用executeOnExecutor 方法,入参就是 sDefaultExecutor,那么这个sDefaultExecutor 是干什么用的呢,他是何时被创建的呢,我们继续来看

   public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
   @UnsupportedAppUsage
   private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

通过源码我们可以看到sDefaultExecutor 这个类就是 SerialExecutor 的对象,并且sDefaultExecutor 是,静态变量在类初始化过程中被创建的,并且共享给所有AsyncTask的实现类,继续看看一下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);
           }
       }
   }

SerialExecutor 中维护了一个队列,当有任务进来时就将这个任务添加到队列里,根据当前是否有任务来判断是否需要执行任务,这里我们可以看到所有添加到SerialExecutor 中的任务是顺序执行的,也就是说如果上一个任务没有执行完毕,下一个任务是不会开启的,如果上一个任务是一个非常耗时的任务,那么你即使执行了AsyncTask 的execute 只是将这个任务放到了任务队列里面,他的执行时机是在前面的任务全部执行完毕后

那么我们如何让当前这个任务立即执行呢, 只需要使用asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,params);这样我们就越过了sDefaultExecutor 他里面的队列,但是这样操作还是有弊端,具体是什么我们来说一下THREAD_POOL_EXECUTOR 这个线程池创建的参数

   private static final int CORE_POOL_SIZE = 1;
   private static final int MAXIMUM_POOL_SIZE = 20;
   private static final int BACKUP_POOL_SIZE = 5;
   private static final int KEEP_ALIVE_SECONDS = 3;

   static {
       ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
               CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
               new SynchronousQueue<Runnable>(), sThreadFactory);
       threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
       THREAD_POOL_EXECUTOR = threadPoolExecutor;
   }

源码中THREAD_POOL_EXECUTOR 的创建是一个静态方法,而他的最大线程数是20个,如果你的任务比较多,而且特别频繁,越过队列直接添加到THREAD_POOL_EXECUTOR中会丢失很多任务,此时我们就需要使用自定义的Executor 来实现具体的业务了,

到此整个过程我们就梳理完毕了,最后再来总结一下AsyncTask工作的具体流程,我们以对象来描述,

AsyncTask 在创建时约定了Params Progress Result 三个的类型,创建了一个Handler 负责发送消息(只发送进度和结果两种消息),这个Handler 可能不是在主线程(如果在创建AsyncTask 时给定的looper 不是MainLooper),同时创建了一个 实现Callable 的WorkRunnable ,他是任务的主体, 还创建了一个可以被打断的 FutureTask,承载着 WorkRunnable ,在我们执行AsyncTask 的execute方法时会将承载任务的FutureTask添加到sDefaultExecutor 他的任务队列中,让工作线程THREAD_POOL_EXECUTOR 一个一个的顺序处理消息,在消息处理过程中或完毕后通过事先创建Hanlder 来给目标线程发送结果

相关文章

网友评论

    本文标题:Android 进阶学习(十一) AsyncTask 源码学习

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