一、AsyncTask简介
为了避免UI线程失去响应的问题,Android建议将耗时操作放在新线程中完成,但新线程也可能需要动态更新UI组件,但新线程不允许直接更新UI组件。在Android的多线程编程当中,我们有两条非常重要的原则必须要遵守:
- 绝对不能在UI Thread当中进行耗时的操作,不能阻塞我们的UI Thread。
- 不能在UI Thread之外的线程当中操纵我们的UI元素。
为了解决该问题,Android提供了如下实现多线程的方法:
- 继承Thread类
- 实现Runnable接口
- Handler
- AsyncTask
- HandlerThread
AsyncTask允许在后台执行一个异步任务。我们可以将耗时的操作放在异步任务当中来执行,并随时将任务执行的结果返回给我们的UI线程来更新我们的UI控件。通过AsyncTask我们可以轻松的解决多线程之间的通信问题。
AsyncTask是Android提供的轻量级的异步类,查看源码发现其内部就是一个Handler和线程池的封装,其实现原理也是基于异步消息处理机制的,只是Android帮我们做好了封装。
二、作用和优点
1.作用
- 实现多线程:在工作线程中执行任务,如 耗时任务。
- 异步通信、消息传递:实现工作线程和主线程(UI线程)之间的通信,将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI操作。
2.优点
- 方便实现异步通信:不需使用 “任务线程+ Handler”的复杂组合;
- 节省资源:采用线程池的缓存线程 + 复用线程,避免了频繁创建和销毁线程所带来的系统资源开销。
三、AsyncTask类
AsyncTask<Params,Progress,Result>
是一个抽象类,通常用于被继承。继承AsyncTask需要指定如下三个泛型参数:
- Params:启动任务时输入的参数类型;
- Progress:后台任务执行中返回进度值的类型;
- Result:后台任务执行完成后返回结果的类型。
需要重写的AsyncTask方法:
doInBackground(Params...)
:必须重写,异步执行后台线程要完成的任务,耗时操作将在此方法中完成。onPreExecute()
:执行后台耗时操作前被调用,通常用于进行初始化操作。onPostExecute(Progress...)
:当doInBackground方法完成后,系统将自动调用此方法,并将doInBackground方法返回的值传入此方法,通过此方法进行UI的更新。onProgressUpdate(Result)
:当在doInBackground方法中调用publishProgress方法更新任务执行进度后,将调用此方法,通过此方法可以知晓任务的完成进度。
总结:在doInBackground()方法中执行具体的耗时任务,在onProgressUpdate()方法中进行UI操作,在onPostExecute()方法中执行一些任务的收尾工作。
其他常用方法:
cancel (boolean mayInterruptIfRunning)
:尝试取消这个任务的执行。execute (Params... params)
用指定的参数来执行此任务(必须在UI线程中调用,因为要将handler绑定到主线程的Looper,方便后台任务执行完毕后,由后台线程切换到UI线程去更新UI)。executeOnExecutor(Executor exec,Params... params)
:用指定的参数行此任务,并运行在指定的线程池中(必须在UI线程中调用)getStatus()
:获得任务的当前状态:PENDING(等待执行)、RUNNING(正在运行)、FINISHED(运行完成)isCancelled()
:在任务正常结束之前能成功取消任务则返回true,否则返回false
三、使用方法
使用步骤:
- 新建内部类继承AsyncTask,并为三个泛型指定参数类型。若不需要指定类型指定为void。
- 根据需要实现AsyncTask的如下方法: doInBackground(Params…)、 onPreExecute()、 onPostExecute(Progress…)、onProgressUpdate(Result)。
- 调用AsyncTask子类实例的excute(Params…params)方法开始执行耗时任务。
使用AsyncTask时必须遵循如下规则:
1.必须在UI线程中创建AsyncTask的实例。
2.必须在UI线程中调用AsyncTask的execute()方法。
3.AsyncTask的doInBackground(Params...)、 onPreExecute()、 onPostExecute(Progress...)、onProgressUpdate(Result)方法,不应由程序员代码调用,而是由Android系统负责调用。
4.每个AsyncTask只能被执行一次,多次调用将会引发异常。
/**
* 步骤1:创建AsyncTask子类
* 注:
* a. 继承AsyncTask类
* b. 为3个泛型参数指定类型;若不使用,可用java.lang.Void类型代替
* c. 根据需求,在AsyncTask子类内实现核心方法
*/
private class MyTask extends AsyncTask<Params, Progress, Result> {
....
// 方法1:onPreExecute()
// 作用:执行 线程任务前的操作
// 注:根据需求复写
@Override
protected void onPreExecute() {
...
}
// 方法2:doInBackground()
// 作用:接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
// 注:必须复写,从而自定义线程任务
@Override
protected String doInBackground(String... params) {
...// 自定义的线程任务
// 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
publishProgress(count);
}
// 方法3:onProgressUpdate()
// 作用:在主线程 显示线程任务执行的进度
// 注:根据需求复写
@Override
protected void onProgressUpdate(Integer... progresses) {
...
}
// 方法4:onPostExecute()
// 作用:接收线程任务执行结果、将执行结果显示到UI组件
// 注:必须复写,从而自定义UI操作
@Override
protected void onPostExecute(String result) {
...// UI操作
}
// 方法5:onCancelled()
// 作用:将异步任务设置为:取消状态
@Override
protected void onCancelled() {
...
}
}
/**
* 步骤2:创建AsyncTask子类的实例对象(即 任务实例)
* 注:AsyncTask子类的实例必须在UI线程中创建
*/
MyTask mTask = new MyTask();
/**
* 步骤3:手动调用execute(Params... params) 从而执行异步线程任务
* 注:
* a. 必须在UI线程中调用
* b. 同一个AsyncTask实例对象只能执行1次,若执行第2次将会抛出异常
* c. 执行任务中,系统会自动调用AsyncTask的一系列方法:onPreExecute() 、doInBackground()、onProgressUpdate() 、onPostExecute()
* d. 不能手动调用上述方法
*/
mTask.execute();
四、其他
1.AsyncTask与Handler的优缺点
AsyncTask与Handler都是为了避免阻塞UI线程工作,都适用于简单的异步处理,相比之下AsyncTask更轻量级(代码上更轻量,实际更耗资源)。
AsyncTask:
- 优点:简单、快捷、过程可控。
- 缺点:在使用多个异步操作并进行UI变更时,就会比较复杂。
Handler:
- 优点:结构清晰、功能定义明确。
- 缺点:在单个后台异步处理时显得代码过多,结构过于复杂。