Android 中耗时操作是在子线程中完成的,子线程不能更新UI,于是便出现了大家熟悉的异步消息处理机制Handler,Handler把在子线程中创建的Message对象发送到消息队列中,经由Looper轮询取出再交由Handler中handleMessage()方法处理。Handler机制完美解决了子线程中有数据却不能更新UI的问题,但是创建Handler对象、重写handleMessage()方法处理消息、创建子线程并创建Message对象显然有些复杂,为了代码更加简单、使用更加方便Android在API 3 推出了AsyncTask这一更简单、好用的异步操作工具。
其实AsyncTask使用的仍然是Handler机制,其内部封装了一个Handler的子类对象InternalHandler对象,关于源码部分后面介绍,下面介绍AsyncTask的使用:
AsyncTask是一个抽象类,其直接父类是Object,我们可以自定义一个类继承AsyncTask,重写其内部的抽象方法doInBackground(Params ... params);
AsyncTask 这个类有三个泛型限定,AsyncTask<Params ,Progress ,Result> 接下来会介绍到它们的作用。
AsyncTask之方法解析一:doInBackground(Params ... params)
1.在onPreExecute()方法执行结束之后该方法会被自动调用;
2.该方法在子线程中执行,通常做一些耗时操作;
3.该方法中的可变参数params是由AsyncTask子类对象调用execute(Params ... params)方法时传递而来,该参数的类型由AsyncTask类上的第一个泛型限定;
4.该方法的返回值将会传递给onPostExecute(Result ... result)方法作为参数使用,返回值类型由AsyncTask类上的第三个泛型限定;
5.在该方法中可以调用publishProgress(Progress ... progress)发布任务完成的进度,该进度值progress将会被发布到主线程onUpdateProgress(Progress ... progress)方法中,作为该方法的参数使用。
AsyncTask之方法解析二:onPreExecute()
1.该方法在主线程中执行;
2.该方法在任务开始前执行,通常用来初始化任务,比如向用户展示一个进度条。
AsyncTask之方法解析三:onPostExecute(Result ... result)
1.该方法在主线程中执行;
2.该方法在耗时操作完成后执行,即在doInBackground(Params ... params)方法结束后执行;
3.该方法的参数result是耗时操作返回的结果,即doInBackground(Params ... params)返回的结果,参数类型由AsyncTask类上的第三个泛型限定;
AsyncTask之方法解析四:onProgressUpdate(Progress ... progress)
1.该方法在主线程中执行;
2.该方法在publishProgress(Progress... progress)每次调用后执行,通常用来向用户展示耗时操作的进度;
3.该方法的参数progress是耗时操作的进度,由publishProgress(Progress ... progress)传递而来,参数类型由AsyncTask类上的第二个泛型限定;
了解完以上方法之后,我们就可以开始写个小demo测试一下啦!
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
private MainActivity context;
private ProgressDialog mDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = MainActivity.this;
MyAsyncTask task = new MyAsyncTask();
task.execute(10);//对应类中第一个泛型Integer
}
/**
* 求和异步任务
*/
class MyAsyncTask extends AsyncTask<Integer, Integer, Long> {
/**
* 初始化任务,展示进度条,在主线程
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.d(TAG, "--- onPreExecute --- " + Thread.currentThread().getName());
mDialog = new ProgressDialog(context);//创建一个进度对话框
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mDialog.show();
}
/**
* 在子线程执行耗时操作,通过publishProgress(Progress ... progress)方法将任务执行的进度传出
* @param params 该参数由task.execute(10);传递而来
* @return
*/
@Override
protected Long doInBackground(Integer... params) {
long sum = 0;
for (int i = 1; i < params[0]; i++) {
sum += i;
publishProgress(params[0], i);//发布进度,参数将传递到onProgressUpdate方法中
try {
Thread.sleep(1000);
Log.d(TAG, "--- doInBackground --- " + Thread.currentThread().getName() + " i == " + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return sum;
}
/**
* 执行在主线程,向用户展示任务的进度
*
* @param values 该参数由publishProgress(Progress ... progress)传递而来
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mDialog.setMax(values[0]);
mDialog.setProgress(values[1]);
}
/**
* 执行在主线程,任务执行完成后的操作,隐藏进度条等
*
* @param aLong 该参数由doInBackground()返回
*/
@Override
protected void onPostExecute(Long aLong) {
super.onPostExecute(aLong);
//返回操作结果 , 进度对话框消失
mDialog.dismiss();
Log.d(TAG, "--- onPostExecute --- " + Thread.currentThread().getName() + " aLong == " + aLong);
}
}
}
这个小demo通过求1-10的和,在doInBackground()方法中通过延时的方式模拟了耗时操作,简单的演示了AsyncTask的使用。
Log日志如下:
02-17 09:40:32.214 8207-8207/com.qj.asynctasktest D/MainActivity: --- onPreExecute --- main
02-17 09:40:33.270 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1 i == 1
02-17 09:40:34.273 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1 i == 2
02-17 09:40:35.277 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1 i == 3
02-17 09:40:36.279 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1 i == 4
02-17 09:40:37.280 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1 i == 5
02-17 09:40:38.281 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1 i == 6
02-17 09:40:39.282 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1 i == 7
02-17 09:40:40.284 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1 i == 8
02-17 09:40:41.286 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1 i == 9
02-17 09:40:42.288 8207-8246/com.qj.asynctasktest D/MainActivity: --- doInBackground --- AsyncTask #1 i == 10
02-17 09:40:42.310 8207-8207/com.qj.asynctasktest D/MainActivity: --- onPostExecute --- main aLong == 55
由Log可看出:
onPreExecute()和onPostExecute()方法都执行在主线程,onProgressUpdate()能够更改UI(更改进度条进度)必然也是主线程。
doInBackground()方法执行在线程池某一线程中,(看过源码后得到的结果,通过Log可以知道该方法没有执行在主线程中)
AsyncTask使用中的注意事项:
1. AsyncTask实例对象必须在UI线程中创建;
2. execute()方法必须在UI线程中调用;
3. 不要手动调用onPreExecute()、doInbackground()、onProgressUpdate()、onPostExecute()这四个方法;
4. 同一个task任务只能被执行一次,尝试执行第二次会抛出异常;
5. execute()执行任务默认是串行的,(API 11之后)多个AsyncTask任务是顺序执行的,如果想并行可以用executeOnExecutor()方法。AsyncTask中为我们提供了一个并行的线程池THREAD_POOL_EXECUTOR。