异步 -- AsyncTask

查看源码 AsyncTask 只是对 ThreadHandler 的一个封装,其中线程是使用线程池技术。

基本概念

1
2
3
4
5
6
7
8
9
10
11
public abstract class AsyncTask<Params, Progress, Result> {
public AsyncTask() {...}
protected void onPreExecute() {...}
protected abstract Result doInBackground(Params... params);
protected void onProgressUpdate(Progress... values) {...}
protected void onPostExecute(Result result) {...}
public final AsyncTask<Params, Progress, Result> execute(Params... params) {...}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {...}
public static void execute(Runnable runnable) {...}
...
}

3 个泛型参数

AsyncTask <Params, Progress, Result>

  • Params
    指定的是我们传递给异步任务执行时的参数的类型。注意:这里是可变长参数,如果只传递了一个参数,使用时为 params[0]
  • Progress
    指定的是我们的异步任务在执行的时候将执行的进度返回给 UI 线程的参数的类型。注意:这里也是可变长参数,如果只传递了一个参数,使用时为 progress[0]
  • Result
    指定的是异步任务执行完后返回给 UI 线程的结果的类型。

我们在定义一个类继承 AsyncTask 类的时候,必须指定好这三个泛型的类型,如果都不指定的,则将其写成 Void

4 个执行步骤

  • onPreExecute()
    UI Thread 当中执行,这个方法是在执行异步任务之前的时候执行,我们可以在异步任务执行前做 UI 提示。
  • doInBackground(Params... params)
    这个方法就是来处理异步任务的方法,执行耗时操作。这个方法也是必须要实现的抽象方法。
  • onProgressUpdate(Progess... values)
    UI Thread 当中执行,用来更新进度条等。
  • onPostExecute(Result... result)
    UI Thread 当中执行,当异步任务执行完之后,将 doInBackground 结果返回给这个方法来更新 UI

2 种执行方式

“执行”必须在主线程中调用,而后台线程的执行方式可以为串行或者并行执行。

  • 串行:execute(Params... params)/execute(Runnable runnable)
  • 并行:executeOnExecutor(Executor exec, Params... params)

注意:各 SDK 版本 execute 默认执行方式不统一,1.5 中顺序执行,1.6 到 2.3 中并行执行,3.0 以后又改回串行执行,并添加并行执行接口 executeOnExecutor

注意事项

  • 必须在 UI 线程中加载和创建,以及调用 execute
  • 不能做特别耗时的操作,建议只几秒内的异步任务
  • 一个 AsyncTask 对象只能被执行一次,即只能调用一次 execute,否则会抛出异常报错:
    Caused by: java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)
  • 不能在程序中主动调用 4 个步骤

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 初始化AsyncTask及执行
protected void function() {
...
// 串行执行,识别一张 bitmap,每次执行前都需要重新 new 一个对象
mClassifierAsyncTask = new ClassifierAsyncTask();
mClassifierAsyncTask.execute(bitmap);
}

// 自定义 AsyncTask 任务类,实现 doInBackground
private ClassifierAsyncTask mClassifierAsyncTask;
private class ClassifierAsyncTask extends AsyncTask<Bitmap , Void, String >{

@Override
protected void onPreExecute() {
super.onPreExecute();
mTvResult.setText(getString(R.string.classifying));
}

@Override
protected String doInBackground(Bitmap... bitmaps) {
if(mMyTfClassifier == null) {
mMyTfClassifier = new MyTfClassifier(MainActivity.this);
}
String result = mMyTfClassifier.recognizeImage(bitmaps[0]);
return result;
}

@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
mTvResult.setText(result);
}
}

源码解析

线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
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 ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);

public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};

private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);

/**
* 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;
}

参数及返回结果封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}

private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;

public AsyncTask() {
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;
}
};

mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
...
postResultIfNotInvoked(get());
...
}
};
}

在构造方法中,传进来的参数 Params 被包装成到 mWorker 中,它是一个 Callable。同时在 mWorker 中定义了返回结果类型 Result,并在 call 中调用了回调方法 doInBackground,执行具体的后台任务。最后 mWorker 包装到 FutureTask 中,当执行完毕后,通过 get() 获取执行的结果,并通知 UI 去更新。

任务执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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) {
...
onPreExecute();

mWorker.mParams = params;
exec.execute(mFuture);

return this;
}

不管是串行还是并行,最终都调用了 executeOnExecutor,这里才真正的把参数传递进来,参数赋值给 mWorker,根据构造方法中参数封装的分析,mFuture 携带了参数和返回值类型,此时只需要调用执行器执行即可 exec.execute(mFuture);

串行执行

示例:mClassifierAsyncTask.execute(bitmap);,串行执行源码分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 1. 串行执行,使用的都是 sDefaultExecutor
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}

// 2. sDefaultExecutor 的定义
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

// 3. SerialExecutor 的实现方式
// 使用双端队列存储,每来一个任务后先入队,确保每次只执行一个任务
// 任务也是交给 THREAD_POOL_EXECUTOR 来执行的
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);
}
}
}

并行执行

示例:mClassifierAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, bitmaps);,并行执行源码见任务执行

1
2
3
4
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;

查看默认线程池定义,THREAD_POOL_EXECUTORstatic final 类型的,可以作为并行 Executor 来使用,或者用户也可以自定义。

存在的问题

Activity 屏幕旋转或销毁时,如果 AsyncTask 没有执行完毕就会存在内存泄露。特别是屏幕旋转时 AsyncTask 没有执行完毕,会导致屏幕异常。

0%