参考资料
鸿洋版AsyncTask
郭霖版AsyncTask
线程池
Android开发艺术探索
Android源码
相关背景知识:
Android消息机制、线程池。
AsyncTask是什么?
(1)AsyncTask是一种轻量级的异步任务类,可以在线程池中执行耗时任务,然后把执行的进度和最终结果传递给主线程并更新UI;AsyncTask封装了Thread和Handler,可以更加方便执行耗时任务,并在主线程中更新UI;
(2)AsyncTask是一个抽象泛型类,提供了Params、Progress和Result三个泛型参数,Params表示参数类型,Progress表示后台任务执行进度的类型;Result表示后台任务结果返回类型;如果不确定传什么参数,那么这三个泛型参数可以用void来代替;
AsyncTask4个核心方法
(1)onPreExecute:在主线程中执行,在异步任务执行之前,此方法可以被调用,一般用于做一些准备工作;
(2)doInBackGround(Params...params),在线程池中执行,用于执行异步任务;params表示异步任务输入参数,在该方法中可以调用publishProgress方法来更新任务进度,该方法会调用到onProgressUpdate中;另外此方法需要将执行结果返回给onPostExcute方法;
(3)onProgressUp0date:在主线程中执行,后台任务执行进度发生改变时此方法被调用;
(4)onPostExcute(Result result):在主线程中执行,result为doInBackgroud返回值;
AsyncTask执行过程
(1)启动方法:new AysncTask().execute();
(2)new AsyncTask()构造方法
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
//执行FutureTask run方法会调用到此处
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
(3)调用AsyncTask.execute()方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
其中sDefaultExecutor是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();去执行下一个任务;
//所以当上一条任务没有执行完,下一个任务是不会执行的;这样AsyncTask中
//所有任务都是串行执行的;
scheduleNext();
}
}
});
if (mActive == null) { //第一次执行时为null;所以会执行scheduleNext();
//第一次执行时,传入的参数为mFuture,所以执行mFuture的run方法;因为
第二次再次执行时mActive不为null所以不会再次进入该方法,该方法尽在首日调用时进入;
scheduleNext();
}
}
protected synchronized void scheduleNext() {
/*任务队列中有任务时,会将任务放到THREAD_POOL_EXECUTOR线程池中去执行;
线程池相关内容请参考相关文章*/
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
其中THREAD_POOL_EXECUTOR为:
//CORE_POOL_SIZE:为核心线程数量,MAXIMUM_POOL_SIZE最大线程数;
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
(4)执行FutureTask run方法:
public void run() {
//该方法在线程池中执行;
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
//在AsyncTask构造方法中初始化FutureTask时,会传入mWorker,mWorker是WorkRunable对象,
WorkRunable实现Callable接口
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//调用AsyncTask mWorker对象的call方法;
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
...
}
}
(5)AsyncTask执行mWorker call方法:
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
//在此时执行AsyncTask doInBackground方法;
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
执行完毕之后,调用postResult方法;
return postResult(result);
}
};
6.执行AsyncTask postResult方法:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
/*InternalHandler发送MESSAGE_POST_RESULT消息,下一步会执行
InternalHandler handleMessage对象;Android消息机制请参考相关文章;*/
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
其中getHandler返回InternalHandler对象,InternalHandler源码如下:
private static class InternalHandler extends Handler {
public InternalHandler() {
//Looper.getMainLooper获取主线程looper,这样
//handler在收到消息并调用handleMessager时可以切换到主线程;
super(Looper.getMainLooper());
}
@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
在收到消息时,调用AsyncTask finish方法;*/
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
/*如果在doInBackgound中执行publishProgress,publishProgress会发送
MESSAGE_POST_PROGRESS,这样会调用AsyncTask onProgressUpdate方法,该方法可以
在UI线程中更新异步任务的进度;*/
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
(7)执行AsyncTask finish方法:
private void finish(Result result) {
if (isCancelled()) {//如果没有调用cancel方法,该方法返回false;
onCancelled(result);
} else {
//执行onPostExcute方法,这样当前任务执行完毕;然后继续回到SerialExecutor中执行finally
中的scheduleNext();继续执行下一个任务;
onPostExecute(result);
}
//将mStatus修改为Status.FINISHED,如果两次执行AsyncTask那么执行到executeOnExecutor方法时,会跑出异常;
mStatus = Status.FINISHED;
}
其他相关问题:
(1)AsyncTask是否必须在主线程创建并执行?
答:创建:在UI线程和子线程都可以创建AsyncTask;
执行:在主线程中执行AsyncTask是正常流程;所以不会有问题;在子线程中执行AsyncTask:1.由于onPreExecute和启动AsyncTask是在同一个线程,所以只要onPreExecute不操作UI就不会有异常;2.onProgressUpdate:publishProgress会获取InternalHandler,并发送MESSAGE_POST_PROGRESS消息;由于InternalHandler使用的是主线程中的looper,所以在执行Handler.handleMessage()时,已经切换到主线程,所以在子线程执行AsyncTask时,onProgressUpdate是可以操作UI控件的;3.onpostExecute同onProgressUpdate;
结论:在主线程和子线程都可以创建AsyncTask,在子线程(只要onPreExecute不操作UI控件)和主线程
都可以执行AsyncTask;
(2)Android开发艺术探索提到AsyncTask中有两个线程池(SerialExecutor、THEAD_POOL_EXCUTOR),对吗?
答:其中SerialExcutor知识一个线程执行的东西,不是线程池,Android开发艺术探索书写有误;
(3)AsyncTask串行执行还是并行执行?
1.AsyncTask在并发执行多个任务时会发生异常。在3.0以前的系统中还是会以支持多线程并发的方式执行,同一时刻能够运行的线程数为5个,线程池总大小为128。也就是说当我们启动了10个任务时,只有5个任务能够立刻执行,另外的5个任务则需要等待,当有一个任务执行完毕后,第6个任务才会启动,以此类推。而线程池中最大能存放的线程数是128个,当我们尝试去添加第129个任务时,程序就会崩溃。
2,通过源码可以看出现在所有任务被执行时,都是先在SerialExcutor进行排队,执行完上一个任务才会执行下一个任务;所以目前AsyncTask中的任务是串行执行的,通过代码实测,其中排队的任务可以有N多个,尝试20000个任务程序也没有崩溃;
3.如果想并行执行可以executeOnExcutor(AsyncTask.THREAD_POOL_EXECUTOR,new AsyncTask());目的就是跳过SerialExecutor排队流程;
(4)为什么子线程不能更新UI控件?
1.因为UI控件不是线程安全的;
(5)为什么不对UI控件的访问加上锁机制?
1.加上锁之后,访问逻辑变的复杂;
2.锁机制降低UI访问效率;
(6)为什么AsyncTask只能执行一次?
因为执行完成后会将mStatus修改为Status.FINISHED,再次执行到executeOnExecutor方法时,如果mStatus为FINISHED,那么就会抛出异常;
网友评论