AsyncTask是android将Handler和Thread继承好的一个类,在某些场景下使用它很方便。先介绍基本用法,然后看一下源代码,最后比较一下它与handler。
7.1 使用方法(点击这里查看官网介绍)
定义:AsyncTask类的定义:publicabstractclass AsyncTask<Params,Progress,Result>{ }
这三个泛型参数的意义:
Params:execute时传入的参数类型
Progress:更新进度时的参数类型
Result:更新完成结果参数类型
如果不需要传入参数的话,使用java.lang.Void代替。
步骤:四个方法
1.onPreExecute():当我们调用execute方法后立马执行,在UI线程上运行,主要用来在设置一些标志。比如显示一个"正在运行中"进度对话框,及时给用户反馈。
2.doInBackground(Params ...):真正在子线程中执行的耗时操作,在onPreExecute方法执行完之后开始执行。可以在这里做一些耗时的操作,比如加载网络图片,操作数据库,读写文件等等。可以将操作进度通过publicProgress(Progress ...)发送给UI线程,然后触发onProgessUpdate(Progress ...)方法。
3.onProgressUpdate(Progress...):当在doInBackground中调用publicProgress之后就会触发此方法,提示用户当前任务的进度。
4.onPostExecute(Result):当doInBackground方法执行完毕后。主要用来更新数据,比如展示下载完成的图片。
看一个简单的例子:class MyAsyncTask extends AsyncTask<Void,Integer,Void>
先看onPreExecute方法:
- proteced void onPreExecute(){
- Log.d(TAG,"onPreExecute,thread id"+Thread.currentThread().getId());
- //一般都是显示进度对话框,提醒用户耐心等待
- dialog.show();
- }
proteced void onPreExecute(){
Log.d(TAG,"onPreExecute,thread id"+Thread.currentThread().getId());
//一般都是显示进度对话框,提醒用户耐心等待
dialog.show();
}
再看doInBackground:
- protected Void doInBackground(Void...params){
- //模拟耗时操作
- for(int i=0;i<6;i++){
- try{
- Thread.sleep(100);
- }
- catch(Exception e){
- }
- //更新界面
- publishProgress(i);//每一次调用此方法,就会触发onProgressUpdate方法。下面会有分析
- Log.d(TAG,"doInBackground,thread id"+Thread.currentThread().getId());
- }
- }
protected Void doInBackground(Void...params){
//模拟耗时操作
for(int i=0;i<6;i++){
try{
Thread.sleep(100);
}
catch(Exception e){
}
//更新界面
publishProgress(i);//每一次调用此方法,就会触发onProgressUpdate方法。下面会有分析
Log.d(TAG,"doInBackground,thread id"+Thread.currentThread().getId());
}
}
如何更新进度:onProgressUpdate:
- protected void onProgressUpdate(Integer...values){
- dialog.setProgress(values[0]);
- Log.d(TAG,"onProgressUpdate,thread id"+Thread.currentThread().getId());
- }
protected void onProgressUpdate(Integer...values){
dialog.setProgress(values[0]);
Log.d(TAG,"onProgressUpdate,thread id"+Thread.currentThread().getId());
}
最后执行完毕:onPostExecute
- protected void onPostExecute(Void result){
- dialog.dismiss();
- Log.d(TAG,"onProgressUpdate,thread id"+Thread.currentThread().getId());
- }
protected void onPostExecute(Void result){
dialog.dismiss();
Log.d(TAG,"onProgressUpdate,thread id"+Thread.currentThread().getId());
}
运行结果:
可以看出,只有在doInBackground中是子线程中运行的,onPreExecute、onProgressUpdate、onPostExecute都是在UI Thread中运行的。这也符合了"不要在非UI Thread中操作UI"的原则(关于UI线程点击这里)。
如何取消任务:
通过调用cancle方法,这样就会使得isCancelled()方法返回true。cancel之后当doInBackground方法执行结束之后执行onCancelled(Result)方法,不再执行onPostExecute(Result)方法。源码实现
- private void finish(Result result) {
- if (isCancelled()) {
- onCancelled(result);
- } else {
- onPostExecute(result);
- }
- mStatus = Status.FINISHED;
- }
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
通常做法是一直检测isCancelled方法返回值,这样能及时终止task。
- for(int i=0;i<10;i++){
- try{
- Thread.sleep(5*1000);
- if(isCancelled()){
- break;
- }
- }catch(Exception e){
- }
- }
for(int i=0;i<10;i++){
try{
Thread.sleep(5*1000);
if(isCancelled()){
break;
}
}catch(Exception e){
}
}
注意:正确使用AsyncTask有几个原则:
1.AsynctTask对象必须是必须是在UI Thread中创建,且execute必须要UI Thread中调用。否则报错:Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()。因为AsyncTask中有Handler(关于Handler请看这里),且该Handler是处理UI线程中的messag队列,所以只能执行在UI线程创建和运行。
2.不要手动调用onPreExecute,doInBackground,onPostExecute等方法。因为AsyncTask的运行依靠的是线程池,handler、message,而这些成员变量的创建和运行时在execute方法中进行的。调用时要使用execute方法,否则也就失去了AsyncTask的作用。关于这个方法下面会介绍
3.不要在doInBackground中修改UI组件,上面已将证明过此方法是在子线程中执行。
4.一个AsyncTask实例只能执行一次,如果执行多次会报错。上源代码:
- 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;
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;
如果AsyncTask实例处于RUNNING或者FINISHED状态,则显示相应的异常。
7.2 源码分析
先看上面所说的几种状态:
- public enum Status{
- PENDING//还没有开始执行
- RUNNING//运行中
- FINISHED//执行完毕
- }
public enum Status{
PENDING//还没有开始执行
RUNNING//运行中
FINISHED//执行完毕
}
我们使用的时候是调用execute方法,所以来看execute方法,该方法执行execute()->executeOnExecutor(sDefaultExecutor , params),看一下executeOnExecutor方法:
- public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
- Params... params) {
- if (mStatus != Status.PENDING) {//如果不是pending状态会产生异常,这也是为什么一个task执行执行一次,请停止了不能再执行
- 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();//立马执行onPreExecute方法
- mWorker.mParams = params;
- exec.execute(mFuture);
- return this;
- }
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {//如果不是pending状态会产生异常,这也是为什么一个task执行执行一次,请停止了不能再执行
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();//立马执行onPreExecute方法
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
可以看出先执行的是onPreExecute方法。当然们也可以调用executeOnExecutor(自定一个executor),3.0之后系统默认通过了一个SerialExecutor用来调度线程池中的线程。
上面出现了mWorker,mFuture以及exec。先介绍mWorker,mFutrue、exec这三个变量。首先是mWorker:
- mWorker = new WorkerRunnable<Params, Result>() {
- public Result call() throws Exception {
- mTaskInvoked.set(true);
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- return postResult(doInBackground(mParams));
- }
- };
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return postResult(doInBackground(mParams));
}
};
如果执行了call方法,就会设置线程优先级,执行postResult(doInBackground(mParams))。此方法在线程池中执行。
然后是mFuture变量
- mFuture = new FutureTask<Result>(mWorker) {
- @Override
- protected void done() {
- try {
- final Result result = get();
- postResultIfNotInvoked(result);
- } catch (InterruptedException e) {
- android.util.Log.w(LOG_TAG, e);
- } catch (ExecutionException e) {
- throw new RuntimeException("An error occured while executing doInBackground()",
- e.getCause());
- } catch (CancellationException e) {
- postResultIfNotInvoked(null);
- } catch (Throwable t) {
- throw new RuntimeException("An error occured while executing "
- + "doInBackground()", t);
- }
- }
- };
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
final Result result = get();
postResultIfNotInvoked(result);
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
}
};
exec类型为:SerialExecutor。只是3.0之后系统默认提供的一个线程池调度器。
- 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);//实际上每次只执行一个线程,3.0之后默认是这种方式,我们可改变这种行为。
- }
- }
- }
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);//实际上每次只执行一个线程,3.0之后默认是这种方式,我们可改变这种行为。
}
}
}
可以看出,将mActive(也就是mWorker)放在一个线程池中运行。也就是doInBackground方法在子线程中执行。最主要的是,这个调度器每次只会执行一个线程。当然我们也可以自己定义一个调度器进行线程的调度。
上面提到mWorker中涉及到postResult方法,再看postResult方法:
- private Result postResult(Result result) {
- Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
- new AsyncTaskResult<Result>(this, result));
- message.sendToTarget();
- return result;
- }
private Result postResult(Result result) {
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
发送一个消息给sHandler处理,sHandler是InternalHandler类型(handler机制无处不在)。
- private static class InternalHandler extends Handler {
- @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;
- }
- }
- }
private static class InternalHandler extends Handler {
@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;
}
}
}
消息类型是MESSAGE_POST_RESULT时调用finish方法:
- private void finish(Result result) {
- if (isCancelled()) {
- onCancelled(result);//如果cancel立马执行onCancelled方法
- } else {
- onPostExecute(result);//否则执行onPostExecute方法
- }
- mStatus = Status.FINISHED;//状态设置为finished
- }
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);//如果cancel立马执行onCancelled方法
} else {
onPostExecute(result);//否则执行onPostExecute方法
}
mStatus = Status.FINISHED;//状态设置为finished
}
该handler还可以处理MESSAGE_POST_PROGRESS,这也证明了publishProgress方法来更新进度 :
- protected final voidpublishProgress(Progress... values) {
- if (!isCancelled()) {
- sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
- new AsyncTaskResult<Progress>(this, values)).sendToTarget();
- }
- }
protected final voidpublishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
发送消息给sHandler,消息类型是MESSAGE_POST_PROGRESS,调用onProgressUpdate方法。
如何调度异步任务:
在3.0之前的系统中调度异步任务使用
运行并发最多128个同时执行,等待队列中最多10个。
3.0之后系统使用是包装过的ThreadPoolExecutor,其实是使用的SerialExecutor来实现。这是一个单线程模型,也就是说每次只执行一个。
当然我们可以指定executor的值,可以查阅相关资料