在研究学习AsyncTask的过程中,在看了源码以及郭霖大神的Android AsyncTask完全解析,带你从源码的角度彻底理解 之后仍然有一段代码不懂。
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);
}
}
}
这段代码我当然能看懂这是一个线性的线程池,将任务一个一个的线性执行,但我不理解的是AsyncTask里也没有提交任务的接口啊,哪来那么多任务的啊?
后来我看到了下面的代码:
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
啊~原来这个默认Executor是static的啊~抱歉抱歉,我脑子瓦特了,也就是说所有继承了AysncTask的类在运行时都是在同一个线程池里的啊~~
然后我做了以下的试验,验证了这个结论:
首先自定义了一个DownloadFilesTask ,用Thread.sleep(5000)模拟下载任务耗时了5秒:
public class DownloadFilesTask extends AsyncTask<String, Integer, String> {
private static final String TAG = "DownloadFilesTask";
private int taskID;
public DownloadFilesTask(int id) {
taskID = id;
}
@Override
protected String doInBackground(String... url) {
Log.d(TAG, "DownLoad Start: TaskID = "+taskID);
try{
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "DownLoad Completed: TaskID = "+taskID;
}
@Override
protected void onProgressUpdate(Integer... progress) {
//setProgressPercent(progress[0]);
}
@Override
protected void onPostExecute(String result) {
Log.d(TAG, result);
}
}
然后在MainActivity中执行:
public class MainActivity extends AppCompatActivity {
private final Executor exec = new ThreadPoolExecutor(15, 200, 10,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DownloadFilesTask task1 = new DownloadFilesTask(1);
DownloadFilesTask task2 = new DownloadFilesTask(2);
//使用默认线性线程池
task1.execute("www.baidu.com");
task2.execute("www.google.com");
/*//使用自定义线程池
task1.executeOnExecutor(exec, "www.baidu.com");
task2.executeOnExecutor(exec, "www.google.com");*/
}
}
使用默认线性线程池的情况下,两个任务应该是线性执行,task1完成后,task2才会开始执行:
11-26 11:27:19.583 26628-26644/com.cr.asynctasktest D/DownloadFilesTask: DownLoad Start: TaskID = 1
11-26 11:27:24.584 26628-26628/com.cr.asynctasktest D/DownloadFilesTask: DownLoad Completed: TaskID = 1
11-26 11:27:24.587 26628-26666/com.cr.asynctasktest D/DownloadFilesTask: DownLoad Start: TaskID = 2
11-26 11:27:29.588 26628-26628/com.cr.asynctasktest D/DownloadFilesTask: DownLoad Completed: TaskID = 2
但如果使用我们自定义的线程池,那么两个任务将是并行执行,运行的总时间将缩减:
11-26 11:29:44.147 26906-26922/com.cr.asynctasktest D/DownloadFilesTask: DownLoad Start: TaskID = 1
11-26 11:29:44.148 26906-26923/com.cr.asynctasktest D/DownloadFilesTask: DownLoad Start: TaskID = 2
11-26 11:29:49.149 26906-26906/com.cr.asynctasktest D/DownloadFilesTask: DownLoad Completed: TaskID = 1
11-26 11:29:49.150 26906-26906/com.cr.asynctasktest D/DownloadFilesTask: DownLoad Completed: TaskID = 2
总结
这样给了我们一个启示,如果你的应用里定义了多个AsyncTask,在一个模块内同时启动了,如果使用了execute方法,那么将是线性执行,总时间将会变长;如果你的模块是时间敏感的,那么最好使用executeOnExecutor方法并行执行,这样运行时间将会缩短,但是有一个问题,你的线程池得依据你的业务小心设计,因为任务之间或许有前后顺序的依赖,或者同时任务太多,线程池大小设定过小,也会发生崩溃。