通过上篇博客对AsyncTask源码进行分析之后,我们对AsyncTask的任务执行有了一个大致了解,但是在那篇博客中我有一个
问题还没有分析到,那就是线程执行体SerialExecutor类,下面我们来看一下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();//真正执行Callable中run方法的地方
} finally {
scheduleNext();//等待线程池中的任务执行完毕后,再往线程池中添加任务
}
}
});
if (mActive == null) {
scheduleNext();
}//第一次被执行后,以后这段代码都不会被执行了。
}
//下面的方法实现的效果是:从队列首部取出任务并删除,然后将取出的任务放入到线程池中。
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);//将任务添加到线程池中
}
}
//上面代码实现了单一线程池的效果,也就是线程池中同一时刻只有一个任务在执行。
}
为空,所以将任务放入线程池中,如果第二次调用时,由于mActive不为空了,所以只是将任务添加到队列中去了,那么这些后来添加的任务怎样才
能放入线程池呢?我们看到在mTask添加匿名Runnable对象时,在其run方法中调用任务执行体r.run之后,最终会调用scheduleNext()方法,也就是
等到任务的执行体,执行完毕以后,才会向线程池中添加下一个任务,这样就实现了单一线程池的效果。
了解了上面的这些东西以后,我在创建AsyncTask对象后,调用其执行方法时,会发现有三个可以进行选择。下面我们就来分析这三个方法的不同
我们实现AsyncTask后,知道那些异步,耗时,网络的操作要放在doInBackground中去执行,但是了解了上面的东西之后,我们可以知道,其实
这些东西也不必放到doInBackground去执行,我们可以自定义Runnable接口,然后将其通过execute(runnable)方法,将其放入线程池中。
execute(),这个方法是将doInBackground的执行体放入到线程池中,而execute(runnable)则会将自定义的执行体放入线程池中,现在我们来模拟
执行多个任务时,会有什么情况,还是使用上篇博客的那个例子
public class AsynActivity extends Activity implements OnClickListener{
Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_asyn);
btn = (Button)findViewById(R.id.asyn);
btn.setOnClickListener(this);
}
@Override
public void onClick(View view) {
TestAsyn asyn = new TestAsyn();
asyn.execute();
TestAsyn.execute(new Task("任务二"));
TestAsyn.execute(new Task("任务三"));
TestAsyn.execute(new Task("任务四"));
TestAsyn.execute(new Task("任务五"));
}
class Task implements Runnable{
String str;
public Task(String str){
this.str = str;
}
@Override
public void run() {
Log.i("task", "任务"+str);
}
}
class TestAsyn extends AsyncTask{
@Override
protected void onPreExecute() {
Log.i("TestAsyn", "onPreExecute");
super.onPreExecute();
}
@Override
protected Result doInBackground(Void... params) {
Log.i("TestAsyn", "doInBackground");
//cancel(true);
//publishProgress(params);
return null;
}
@Override
protected void onPostExecute(Result result) {
Log.i("TestAsyn", "onPostExecute");
super.onPostExecute(result);
}
@Override
protected void onProgressUpdate(Void... values) {
Log.i("TestAsyn", "onProgressUpdate");
super.onProgressUpdate(values);
}
@Override
protected void onCancelled() {
Log.i("TestAsyn", "onCancelled");
super.onCancelled();
}
@Override
protected void onCancelled(Result result) {
Log.i("TestAsyn", "onCancelled");
super.onCancelled(result);
}
}
}
从执行结果中我们可以看出,每次运行的结果是不同的,但是有一点是确定的,那就是任务添加的顺序就是结果的输出顺序,这也印证了
之前对源码中的单线程线程池的分析是对的。同时我们还可以看出:
一、无论添加多少个任务onPreExecute方法是第一个被执行的,
二、onPostExecute方法的执行和自定义的Runable无关,只和excute()方法有关,也就是只有调用excute()方法onPostExecute
方法才会执行,调用execute(new Task("二"))方法这个方法是不会执行的。
上面是对execute()和execute(runnable)方法进行的分析,那么executeOnExecutor(exec, params)方法呢?他有什么作用呢?
从executeOnExecutor(exec, params)方法中传进的参数我们可以出,他是自定义了一个Executor接口对象,也就是自定义了
SerialExecutor类,没有使用默认的SerialExecutor类。所以如果我们不想要这个单一线程池的效果,那么我们可以自定义一个
线程池,然后根据我们自己的需要去执行任务,如下代码:
@Override
public void onClick(View view) {
TestAsyn asyn = new TestAsyn();
asyn.executeOnExecutor(new TestExecute());
asyn.execute();
TestAsyn.execute(new Task("任务二"));
TestAsyn.execute(new Task("任务三"));
}
class TestExecute implements Executor{
@Override
public void execute(Runnable r) {
//这里还没有实现,所以上面的任务放进来不会被执行
}
}
在这个类中我们可以根据我们的需要安排任务的执行顺序,这就给了我们很大的自由。
以上就是AsyncTask多任务以及其自带的三个执行方法的解析。至此AsyncTask的分析就已经全部完成了。