前言
AsyncTask 在Android开发中是一个经常用到的类,允许用户在工作线程上完成后台计算等任务,之后将结果同步UI线程,比起 Thread 和 Handler 模型使用起来方便一些。
AsyncTask 使用起来如此方便了,那么有什么需要注意的问题么?看一段AsyncTask官网的介绍文档:
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with Build.VERSION_CODES.DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with Build.VERSION_CODES.HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
可以看出,从 Android1.6 到 Android2.2 之间,AsyncTask是并行执行的,从HONEYCOMB(2.3)开始,为了避免并行执行造成的通用应用错误,任务的执行方式修改为串行。
分析
我们从最新的Android 8.0中分析一下AsyncTask:
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
AsyncTask中定义了一个线程池,在我们执行execute(params)的时候的操作如下:
@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;
}
其中Executor传递了一个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);
}
}
}
这里应该比较明确了,默认的executor使用的 SerialExecutor,而 SerialExecutor 的 execute 中的逻辑也很简单,直接添加队列,如果第一次添加的话,从队列中取出一个任务执行,进而达到了串行执行的结果。
到此,AsyncTask 为什么串行执行已经分析完毕。
使用
当前主流的应用,如手机淘宝、支付宝等的最低支持版本均已经是4.0,所以在开发中,系统的默认的AsyncTask的执行时串行的,我们可以进行修改。上面分析中也有提到,在AsyncTask执行execute方法的时候,使用了默认的Executor,我们可以使用AsyncTask中提供的另外一个方法 executeOnExecutor 指定线程池来执行,需要注意的一点是AsyncTask提供了线程池的 AsyncTask.THREAD_POOL_EXECUTOR ,我们可以使用这个线程池,但是这个线程池的参数中的队列长度是128,线程池拒绝策略采用的 AbortPolicy ,任务超出线程池可承受范围(MAXIMUM_POOL_SIZE + POOL_SIZE)时,将会发生异常。
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
//线程池队列
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
如果我们使用这个线程池,添加了比较多的耗时任务,将会crash,测试代码如下:
package me.codeboy.test;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new LinkedBlockingDeque<>(128));
for (int i = 0; i < 200; i++) {
new AsyncTask<Integer, Void, Void>() {
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
@Override
protected Void doInBackground(Integer... params) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (params != null || params.length > 0) {
Log.e(MainActivity.this.getClass().getSimpleName(), "" + params[0]);
}
return null;
}
}.executeOnExecutor(threadPoolExecutor, i);
}
}
}
错误信息:
java.lang.RuntimeException: Unable to start activity ComponentInfo{me.codeboy.test/me.codeboy.test.MainActivity}: java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@a1014c4 rejected from java.util.concurrent.ThreadPoolExecutor@2c4bfad[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 0]
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@a1014c4 rejected from java.util.concurrent.ThreadPoolExecutor@2c4bfad[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2078)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:843)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1389)
at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:651)
at me.codeboy.test.MainActivity.onCreate(MainActivity.java:50)
at android.app.Activity.performCreate(Activity.java:6975)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1213)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2770)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
避免的方法是可以将队列大小加大或者不限制大小,在不限制大小的时候,如果MAXIMUM_POOL_SIZE和CORE_POOL_SIZE不同,那么MAXIMUM_POOL_SIZE将失去意义。
如有任何知识产权、版权问题或理论错误,还请指正。
转载请注明原作者及以上信息。