AsyncTask
1.定义,介绍:
- asynctask是Android中的一个自带的轻量级异步类,通过他可以轻松的实现工作线程和UI线程之间的通信和线程切换(其实也只能在工作和ui线程之间切换,稍后会提到)
- asynctask是一个抽象类,所以我们需要创建他的子类,一般重写他的四个方法即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//这个就是要在后台做的工作,他将运行在后台工作线程上
@WorkerThread
protected abstract Result doInBackground(Params... params);
//这个时开始执行前的操作,运行在主线程上
@MainThread
protected void onPreExecute() ;
//doInBackground完成后调用
@MainThread
protected void onPostExecute(Result result)
//实时更新,通过在doInBackground中调用publishProgress()方法
@MainThread
protected void onProgressUpdate(Progress... values)
2.源码
1.创建:
- 问题:为什么说只能在工作线程和UI线程之间通信?我自己建一个looper线程也不行吗?(当然这种情况也很少)
看他的三个构造函数:可以看出不管调用哪个都会指向第三个构造方法,关键代码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
27public AsyncTask() {
this((Looper) null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
...
...
...
}
`mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
所以,如果调用第一个构造函数,那么传入的Looper为null;如果调用第二个则传入handler.getLooper(),如果他为MainLooper的话,也将和第一个构造函数一样进入了 getMainHandler()方法:创建了一个拥有主线程的InternalHandler? getMainHandler() : new Handler(callbackLooper); `
那如果他不是mainlooper的话,就创建了一个new Handler(callbackLooper),然而这个handle既没有传入callback,也没有重写其handleMessage方法,其实是一个空的handle,但他可以调用msg的callback呀。就算这样,那我是不是仍然可以创建一个不与主线程(UI线程)相连的handle,那为啥还说只能用在ui与工作线程之前呢?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
29private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
哈哈,请再看一下上面贴上的三个构造函数的源码,我专门把第二个和第三个的注释也贴了上去,有一个很重要的注解@hide
,也就是说google把第二个和第三个构造函数给隐藏起来了,不对开发者开放,我们根本访问不到(虽然能看到,而且时public,感兴趣可以查一下这个注解)。所以我们只能用第一个构造函数,传入空的looper,从而调用getMainHandler(); - 顺便提一下handle 的执行顺序(dispatchMessage)
- msg的callback不为空,调用handleCallback方法(message.callback.run())
- mCallback不为空,调用mCallback.handleMessage(msg)
- 最后如果其他都为空,执行Handler自身的 handleMessage(msg) 方法
2.执行:
- 包装
doInBackground
方法。
创建完handle后,在第三个构造函数中可以看出,他接着创建了work(实现了callable)和futur(FutureTask对象其实就是更方便操作线程,Futrue可以监视目标线程调用call的情况,当你调用Future的get()方法以获得结果时,当前线程就开始阻塞,直接call方法结束返回结果。)其中很关键的代码就是在work中调用了,然后又将work用futuretask封装,在之后的调用中会最后提交给线程池result = doInBackground(mParams);
mFuture = new FutureTask<Result>(mWorker) {......}
最后在finaly里调用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18mWorker = 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;
}
};postResult(result)
,将结果传递给handle,将调用handle(InternalHandler)的handleMessage,他处理两个,一个是执行结束finish()
,从而调用了onPostExecute
,另一个是在doInBackground里用户调用的publishProgress像handle发送消息,从而调用onProgressUpdate
1 | public void handleMessage(Message msg) { |
asynctask的两个线程池:
- `public static final Executor THREAD_POOL_EXECUTOR`:
1 | private static final BlockingQueue<Runnable> sPoolWorkQueue = |
可以看出,这是一个核心线程数最小为2的线程池,它是用来并行执行task的,但当达到核心线程最大值后,依旧会在阻塞队列里等待
public static final Executor SERIAL_EXECUTOR = new SerialExecutor()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25private 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:每次将任务提交到数组双向队列里,execute后会依次执行,然后scheduleNext获取下一个任务。
AsyncTask中默认时使用的第二个,SERIAL_EXECUTOR,串行执行,
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
当然你也可以修改1
2
3
4/** @hide */
public static void setDefaultExecutor(Executor exec) {
sDefaultExecutor = exec;
}然而很不幸,他也被hide起来了…但是可以在接下来的入口处,直接调用
executeOnExecutor(Executor exec,Params... params)
方法传入指定的Executor
开始执行的入口:
当创建完asynctask后,调用asynctask.execute(Params… params)就可开始执行,然后其实是调用了executeOnExecutor(Executor exec,Params... params)
这个方法,传入 sDefaultExecutor和doInbackgroud方法的参数:
1 | public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, |
四个回调执行的线程
:
- `onPreExecute()`:
从上面代码我们可以看到在这里执行了 onPreExecute(),所以,在哪个线程执行了asynctask.execute(Params… params), onPreExecute()他就在哪个线程执行
- doInBackGround()
:被放到了asynctask的线程池里执行
- onProgressUpdate()和onPostExecute()
这两个都是在handler的回调handleMessage里被调用的,所以关键就在handler里的Looper是哪个线程的,他们俩就在哪个线程执行(注意,这里其实跟handler在哪个线程创建没关系,可以在子线程创建handler而传入主线程的Looper)而我们之前也分析了,现在handler的looper只能是主线程的 ,所以这两个方法也是在主线程执行的
如:
1 | Handler handler=new Handler(getMainLooper(), new Handler.Callback() { |
下面是我的测试例子:
1 |
|
1 | Activity里: |
结果:
所以,asynctask也不一定非得在主线程创建和执行
最后一问:为啥一个任务只能执行一次?
当我第一次看到这个规则时,我也很奇怪,好不容易创建了,就执行一次就完了?我不信,结果看一下源码确实发现了那个异常,如果是同一个任务就会抛出“通一个任务只能执行一次”,那意义何在呢?
在仔细看一下源码发现他的线程池都是静态的,所以这样更好诠释了asynctask是一个轻量的线程框架的含义,每次没给他其实就代表一次任务,提交给统一的线程池去管理运行。
1 |
为什么 AsyncTask 的对象只能被调用一次,否则会出错?(每个状态只能执行一次)
从上面我们知道,AsyncTask 有 3 个状态,分别为 PENDING、RUNNING、FINSHED,而且每个状态在 AsyncTask 的生命周期中有且只执行一次。由于在执行完 execute 方法的时候会先对 AsyncTask 的状态进行判断,如果是 PENDING(等待中)的状态,就会往下执行并将 AsyncTask 的状态设置为 RUNNING(运行中)的状态;否则会抛出错误。AsyncTask finish 的时候,AsyncTask 的状态会被设置为 FINSHED 状态。
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)”);
}
}