目录

AsyncTask?AsyncTask串行and并行?

一、前言

  • AsyncTask,相信你不会陌生,也许你很幸运,早已了解了AsyncTask这个家伙挖的坑,也许你已经被坑过了,也许你没坑了,然而还没有发觉!
  • 本次笔者将带大家一起来看下AsyncTask这个坑是如何挖出来的。
  • 啥也不说啦,我们先来段代码看看 1、首先创建一个AsyncTask类

http://upload-images.jianshu.io/upload_images/1811893-e85ad0f56cb5b6cb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 2、假设你这样运行

new MyAsyncTask("MyAsyncTask#1").execute();
new MyAsyncTask("MyAsyncTask#2").execute();
new MyAsyncTask("MyAsyncTask#3").execute();
new MyAsyncTask("MyAsyncTask#4").execute();
new MyAsyncTask("MyAsyncTask#5").execute();
new MyAsyncTask("MyAsyncTask#6").execute();

3、你觉得会发生什么呢?(。ì _ í。)

  • 在Android3.0之前执行的时间是一样的,在Android3.0之后每个执行时间相差2秒
  • android3.0虽然已经基本不用适配了,但是我们了解一下还是不错的哦

二、AsyncTask知识补充(如果你对AsyncTask已经很熟了,可以跳过该章节)

  • 在讲解AsyncTask的坑之前,我们先了解下AsyncTask的源码,有助于我们理解为什么他是个坑,我们应该如何处理这个坑,当然,你也可以直接跳到下一章节。
  • 系统为了帮助开发者简化线程的切换问题,很人性化的提供了轻量级的AsyncTask。AsyncTask的发展经历了几次的修改,进而为我们挖下了一个很容易被忽略掉的坑。
  • AsyncTask作为一种轻量级的异步任务类,在Android开发过程中颇受开发者的喜爱。使用AsyncTaskNike 以在线程池中之行后台任务,并把之行的进度和最终结果传递给UI线程做进一步的操作。
  • 本质上,AsyncTask也是封装了Thread和Handler,前面我们在 Android 开发之Handler的前世今生中通过Handler的源码分析并学习了Handler的实现流程。有需要的小伙伴可以点击一下,有不好的地方欢迎留言哦。
  • 大家都知道AsyncTask是一个抽象类,一般我们继承AsyncTask来创建我们的AsyncTask,而创建的时候我们需要提供三个泛型参数(~~||我怀疑我是不是在瞎逼逼了), 这三个泛型参数跟AsyncTask关系不浅哦。 1、Params:表示我们在调用execute方法时传递的参数类型 2、progress:表示后台任务的执行进度类型 3、Result:表示后台任务的返回结果类型。 不需要的的数据可以设置为Void哦
  • AsyncTask也为我们提供了如下方法: 1、onPreExecute():该方法会在主线程中被调用,你可以在该方法内部编写一些如显示加载动画的逻辑代码 2、doInBackground:该方法在子线程中执行,一般在该方法中编写需要在子线程中执行的操作。如果你有更新进度的需求,可以通过调用publishProgress方法,该方法会调用onProgresssUpdate方法,调用publishProgress方法时需要传递一个进度值,该值最终会在onProgresssUpdate中调用,为什么要这样做呢?因为onProgresssUpdate实在主线程中执行的,只有主线程才可以更新UI哦。当然执行doInBackground时你需要返回一个结果集。该结果集最终在onPostExecute中获取。同样的原因,onPostExecute是在主线程中运行的。 ╮(╯_╰)╭(线程总是切换来切换去,真烦是不是?) 3、onProgresssUpdate:上面讲过了,这里就略过了 4、onPostExecute:同样onPostExecute也是, 5、onCancelled 在主线程中执行。当异步任务被取消时被毁掉。此时onPostExecute则不会被调用了哦

三、AsyncTask使用注意事项

  • 1、AsyncTask类第一次加载必须在主线程中加载(这个不需要我们关系,ActivityThread已经帮我们做了)
  • 2、AsyncTask对象的创建代码必须在主线程中调用
  • 3、execute方法必须在主线程中调用
  • 4、onPreExecute、onPostExecute、doInBackground、onProgressUpdate 四个方法不能在程序中直接调用
  • 5、一个AsyncTask实例只能调用一次execute方法。
  • 6、AsyncTask执行execute方法时在Android1.6之前串行;Android1.6之后并行;Android3.0后串行

四、以日常使用为起点分析AsyncTask的源码(^V^)

  • Ok,假设你使用AsyncTask时都是使用execute方法来调用,那么我们就从他入手吧!~^_^

  • 我们进入execute的方法体看下,里面做了什么呢

      @MainThread
      public final AsyncTask<Params, Progress, Result> execute(Params... params) {
      	return executeOnExecutor(sDefaultExecutor, params);
      }
    
  • 我先我们看到一个@MainThread的注解。╮(╯_╰)╭这是什么东东呢?我没用过呢。

  • 其实它是google提供的一个注解,标注我们的这个方法必须在主线程中调用,如果不是的话,AS就会红色提示我们哦。

  • 然后我们可以看到,execute里面其实是执行了executeOnExecutor方法,他传入了两个参数:1、sDefaultExecutor(这是干什么的呢?)2、params 从execute传递下来的可变参数,OK,重点就是sDefaultExecutor了,从名字上看sDefaultExecutor是个磨人的执行器的意思,但我们是很较真的人,所以必须看下sDefaultExecutor是个什么东东( ̄∇ ̄),所以就逗逼逗逼的去找相关代码了

      public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    
      private static final int MESSAGE_POST_RESULT = 0x1;
      private static final int MESSAGE_POST_PROGRESS = 0x2;
    
      private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    
  • Ok,从其定义的地方可以看到,sDefaultExecutor是一个Executor类型的实例,通过SerialExecutor()获得。我们下看SerialExecutor()里面都做了什么?

      private static class SerialExecutor implements Executor {
      	//1、线程队列
      	final ArrayDeque mTasks = new ArrayDeque();
      	//2、当前执行的线程
      	Runnable mActive;
    
      	public synchronized void execute(final Runnable r) {
      		//3、创建一个新的runable入队列
      		mTasks.offer(new Runnable() {
      			public void run() {
      				try {
      				//5、这里有点绕,首先新创建的runnable会先调用我们外部的runnable,外部的runnable执行完了再调用scheduleNext(),外部的runnable其实就是我们调用时传入的runnable,我们在线程中编写的代码在调用r.run()时开始执行。mTasks中的runnable只是对我们的runnable做了一次包装
      					r.run();
      				} finally {
      					scheduleNext();
      				}
      			}
      		});
      		//4、如果当前没有正在执行的线程任务,则马上调用scheduleNext()方法取出队列中的runnable并运行。
      		if (mActive == null) {
      			scheduleNext();
      		}
      	}
    
      	protected synchronized void scheduleNext() {
      		//6、线程出队列,如果队列部位空,就通过THREAD_POOL_EXECUTOR.execute(mActive);线程池执行runable。
      		if ((mActive = mTasks.poll()) != null) {
      			THREAD_POOL_EXECUTOR.execute(mActive);
      		}
      	}
      }
    
  • 从上面代码可以看到,里面有一个mTasks的数组队列。当我们调用Execute的时候就会在mTasks队列中插入一个runnable实例,也就是说,mTasks里面存放的是线程,可能你会很奇怪,首先这里public synchronized void execute(final Runnable r)传如了一个runnable,然后这里mTasks.offer(new Runnable() 又创建一个新的runnable入队列。

  • OK,更详细的分析我放到了代码中

  • OK我们继续回到executeOnExecutor方法

      @MainThread
      public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
      		Params... params) {
      		//1、判断任务是否没有被执行过,mStatus != Status.PENDING为true为被执行过,这也就是前面说的execute只能执行一次的原因,如果执行过下面会报运行时异常
      	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)");
      		}
      	}
      	//2、设置任务状态为正在执行
      	mStatus = Status.RUNNING;
      	//3、先调用onPreExecute方法
      	onPreExecute();
    
      	mWorker.mParams = params;
      	//4、通过执行器调用我
      	exec.execute(mFuture);
    
      	return this;
      }
    

小结:

  • 通过上面代码,我们知道,在执行AsyncTask的execute方法时,我们的线程默认是放入一个ArrayQueue队列中串行的执行的。也就是sDefaultExecutor是一个串行的执行器,所以我们的AsyncTask默认是串行自行的。

  • Ok,我们来进一步分析下AsyncTask的执行过程,首先我们看下AsyncTask的构造方法都做了什么

      public AsyncTask() {
      	//1、
      	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;
      		}
      	};
      	//2、
      	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);
      			}
      		}
      	};
      }
    
  • 1处:创建一个WorkerRunnable,注意第二个泛型参数Result,这个就是我们继承AsyncTask时传递的第三个参数哦,然后里面看到调用了result = doInBackground(mParams);最终都会调用postResult(result);方法

  • 2处:创建一个FutureTask的对象,mFuture就是我们前面executeOnExecutor方能发中最重调用exec.execute(mFuture);传入的参数哦,说明mFuture是个runnable类型的实例。然后把mWorker传入创建,说明runable执行的是mWorker里面的工作,yes,所以我们的doInBackground方法是在runable线程中执行的

  • OK,我们之前只说了我们的代码是如何放入子线程中的,但是并没有看见启动子线程的代码啊?我们可以看奥,他最终都调用的postResult方法

      private Result postResult(Result result) {
      	@SuppressWarnings("unchecked")
      	Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
      			new AsyncTaskResult<Result>(this, result));
      	message.sendToTarget();
      	return result;
      }
    
  • Ok,可以看到,里面是通过我们熟悉的Handler来进行实现的哦。我们看他他传入了MESSAGE_POST_RESULT和AsyncTaskResult,this就是我们的AsyncTask了,后面应该是在handler中调用了AsyncTask的某个方法,我们看下getHandler()

      private static Handler getHandler() {
      	synchronized (AsyncTask.class) {
      		if (sHandler == null) {
      			sHandler = new InternalHandler();
      		}
      		return sHandler;
      	}
      }
    
  • OK,里面是一个InternalHandler实例。而InternalHandler是这样的

      private static class InternalHandler extends Handler {
      	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:
      				// There is only one result
        result.mTask.finish(result.mData[0]);
      				break;
      			case MESSAGE_POST_PROGRESS:
      				result.mTask.onProgressUpdate(result.mData);
      				break;
      		}
      	}
      }
    
  • OK,里面看到了MESSAGE_POST_RESULT对应的是finish方法。也就是我们的AsyncTask的finish方法

      private void finish(Result result) {
      	if (isCancelled()) {
      		onCancelled(result);
      	} else {
      		onPostExecute(result);
      	}
      	mStatus = Status.FINISHED;
      }
    
  • 可以看到,里面最终会调用onCancelled(result);或者onPostExecute(result);

小结:

  • 创建 AsyncTask是会创建mWorker和mFuture,mFuture是一个runable类型的对象,最终会在我们执行execute-》executeOnExecutor时,传入执行队列中等待执行。在传如前先调用了onPreExecute()方法,在mFuture被执行的时候,会回调mWorker的call方法,call方法里会调用doInBackground方法,获得doInBackground的执行结果后调用postResult方法,postResult内部通过handler切换线程,最终调用我们的finish方法,finish里面会调用onCancelled(result)或者onPostExecute(result);中的一个。这样我们的AsyncTask的一个关键流程就走完了。

五、我想让AsyncTask并行执行,可以么?

  • 当然可以,我们前面分析了,串行还是并行,关键是执行器。因为默认传入的是sDefaultExecutor,sDefaultExecutor是个串行的执行器,所以我们传入一个并行的执行器,是不是就可以了呢?

  • 我们可以看到AsyncTask也提供了一个THREAD_POOL_EXECUTOR,根据注解,使用它可以让execute 并行工作

    /**

    • An {@link Executor} that can be used to execute tasks in parallel. */public static final Executor THREAD_POOL_EXECUTOR;
  • 看到这里我有点郁闷了,前面在分析SerialExecutor是,里面不也是将runable让入THREAD_POOL_EXECUTOR中执行么?

  • SerialExecutor为什么是串行呢?虽然也是放入THREAD_POOL_EXECUTOR中,但是他是从队列中取出来放入的哦,而且一次只取出一个,执行完再取第二个

总结:

  • 通过本次学习,我们了解了AsyncTask从创建到执行返回结果的工作流程,也明白了为什么默认时串行执行的,当然,改为并行也是非常简单的,你可以自己自定义执行器,自己来控制,但是一般都不需要的( ̄∇ ̄);通过本次学习,相信在使用AsyncTask时,你会更加踏实。厚积而薄发!