1、一个AsyncTask对象只能执行一次任务,否则会报错。
private void test() {
MAsyncTask asyncTask = new MAsyncTask();
for (int i = 0; i < 3; i++) {
asyncTask.execute("456" + i);
}
}
class MAsyncTask extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... strings) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Loger.e("doInBackground--->" + strings[0]);
return "result";
}
}
这里执行会报错,报错原因是因为在executeOnExecutor方法中会检查AsyncTask的状态,一旦AsyncTask的Status状态不是PENDING就不可以执行executeOnExecutor方法。所以会报错,报错是要不正在执行,要不就是已经执行过了,一个任务只能执行一次。
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;
}
2、AsyncTask.execute()和AsyncTask.executeOnExecutor()的区别
1)先看AsyncTask.execute()方法,默认使用了sDefaultExecutor 参数
for (int i = 0; i < 5; i++) {
new MAsyncTask().execute("execute--->" + i);
}
执行的结果是:
04-22 14:01:32.345 5189-5230/E/Loger: doInBackground--->execute--->0
04-22 14:01:35.345 5189-5230/ E/Loger: doInBackground--->execute--->1
04-22 14:01:38.346 5189-5230/ E/Loger: doInBackground--->execute--->2
04-22 14:01:41.348 5189-5230/E/Loger: doInBackground--->execute--->3
04-22 14:01:44.350 5189-5230/E/Loger: doInBackground--->execute--->4
从上面的结果能够看出execute执行任务都是串联的,不能并行处理任务。为什么新建了5个AsyncTask对象,执行任务是串行的,执行时间是14:01:32依次增加,主要是因为AsyncTask中定义的成员变量sDefaultExecutor 是静态的,所有的AsyncTask对象都是共享的
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);
}
}
}
所以当所有的任务执行的时候会共享一个SerialExecutor对象,在SerialExecutor中使用的双端队列,只能排序执行了。
2)AsyncTask.executeOnExecutor()执行实例
for (int i = 0; i < 5; i++) {
new MAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
"executeOnExecutor--->" + i);
}
04-22 14:16:48.851 5353-5396/E/Loger: doInBackground--->executeOnExecutor--->1
04-22 14:16:48.851 5353-5399/E/Loger: doInBackground--->executeOnExecutor--->4
04-22 14:16:48.851 5353-5395/E/Loger: doInBackground--->executeOnExecutor--->0
04-22 14:16:48.852 5353-5398/E/Loger: doInBackground--->executeOnExecutor--->3
04-22 14:16:48.852 5353-5397/E/Loger: doInBackground--->executeOnExecutor--->2
这里直接使用了AsyncTask的内部的线程池对象,也可以自己使用,但是输出的结果能够看出,执行任务的时间是并行的,都是在14:16:48执行的,这里就是直接调用了线程处理。
所以当需要串行执行任务的时候使用AsyncTask.execute(),当需要并行执行多个任务的时候使用AsyncTask.executeOnExecutor()。
3、AsyncTask带来的内存泄漏
1)内部类AsyncTask静态化
为什么当内部定义的AsyncTask需要静态化,其实这与AsyncTask本身没有多大的关系,先谈一下内部类静态化和不静态的区别
非静态内部类:当MAsyncTask定义在Activity的内部的时候,MAsyncTask生存是一直依赖Activity的,可以理解为Activity的内部成员一样,当外部调用finish()结束Activity时,由于MAsyncTask依赖Activiy,持有Activity的引用,造成Activity不能回收。
静态内部类:当MAsyncTask定义为static的时候,内部静态类就等同于外部定义的单独的一个类,与Activity本身已经脱离了关系。这样就不会造成MAsyncTask依赖于Activity。
2)弱引用的使用
当AsyncTask中需要Activity的对象的时候,如果AsyncTask持有Activity对象,当Activity退出了,而此时AsyncTask才刚刚进行到了一半,这时候线程还得继续执行,但是Activity对象一直不能及时的回收,本应该在Activity结束了就应该被回收的。这就引发了内存的泄漏。
private void test() {
new MAsyncTask(new WeakReference<Activity>(TestActivity.this)).execute("execute--->" + 0);
}
class MAsyncTask extends AsyncTask<String, Void, String> {
private Activity mActivity;
public MAsyncTask(WeakReference<Activity> weakReference) {
this.mActivity = weakReference.get();
}
}
对于WeakReference包装的对象,如果这时GC运行, 那么这个对象就会被回收,不论当前的内存空间是否足够,这个对象都会被回收,这样就避免了内存你的泄漏。
所以使用AsyncTask时,使用内部类就使用static进行修改,要不就单独写在外面,对于Activity作为参数时,传递弱引用参数作为传递的对象,这样就避免了内存的泄漏。
总结:
1、每一个AsyncTask对象只能执行一个任务
2、AsyncTask.execute()是串行执行多任务(按序执行),AsyncTask.executeOnExecutor()可以多任务同步执行
3、AsyncTask使用时定义为静态的,持有Activity引用采用弱引用进行包装,再提供给AsyncTask使用