AsyncTask介绍及使用
AsyncTask是什么?异步任务,在android中主线程不能做耗时的操作,否则很容易出现ANR异常,所以像访问网络,读取数据库等一些耗时的操作都放在子线程中,但是子线程是无法更新UI的,我们还需要把得到的结果发送到主线程中做更新UI的处理。这时我们就需要用到Handler或者AsnycTask,其实AsyncTask是对Handler和Thread做了封装,更加方便调用。
1. AsyncTask API介绍
- 构造方法
public abstract class AsyncTask<Params,Progress,Result>
//三个泛型分别表示: 启动任务执行的输入参数 , 后台任务执行的进度 , 后台任务执行结果
- 普通方法
(1). execute(Params... params)
// 调用该方法来启动执行异步方法
(2). onPreExecute() ;
// 在execute(Params... params)方法调用之后立即执行的方法,该方法在主线程中执行,
(3). doInBackground(Params... params);
// 在onPreExecute()执行之后执行的方法,该方法在子线程中执行,可以用来做一些耗时的操作;在该方法中可以调用publishProgress(Progress... vallues)来更新进度等,
(4). onProgressUpdate(Progress... values);
// 在调用publishProgress(Progress... values),该方法在UI线程中执行,用来更新进度
(5). onPostExecute(Result result);
// 后台执行结束时,该方法调用,在UI线程中执行。后台计算结果被作为参数传递给该方法。
(6). onCancelled();
// 当任务取消时调用该方法
- 使用时注意事项:
(1). 异步任务的实例对象必须在主线程中创建;
(2). execute(Params… params)方法必须在主线程中调用;
(3). 不要手动调用onPreExecute(),doInBackground(Params… params),onPostExecute(Result result)这几个方法;
(4). doInBackground(Params… params)方法是在子线程中执行的,所以不能做更新UI的操作;
(5). 一个异步任务实例只能执行一次,如果 第二次就会抛出异常;
2. 使用实例
public class MainActivity extends Activity implements View.OnClickListener {
private ProgressBar pb ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pb = (ProgressBar) findViewById(R.id.pb);
pb.setMax(100);
findViewById(R.id.btn1).setOnClickListener(this);
}
@Override
public void onClick(View v) {
asyncTask.execute(); // 调用execute()方法启动异步任务;
}
private AsyncTask<Void,Integer,String> asyncTask = new AsyncTask<Void, Integer, String>() {
@Override
protected void onPreExecute() {
showToast("调用execute()方法执行异步任务,该方法立即在主线程中执行");
}
@Override
protected String doInBackground(Void... v) {
for(int i=0 ;i<=100;i++){
SystemClock.sleep(500); // 模拟耗时操作
publishProgress(i); //调用该方法更新进度
}
return "执行完成";
}
@Override // 该方法在调用publishProgress()方法调用之后执行,执行在主线程接收publishProgress()传递的参数,用来更新UI界面
protected void onProgressUpdate(Integer... values) {
pb.setProgress(values[0]);
}
@Override // 该方法在doInBackground()方法执行结束后执行,接受其传递的参数,该方法在主线程中执行
protected void onPostExecute(String s) {
showToast(s);
}
};
/**
* 显示Toast
* @param msg
*/
private void showToast(String msg){
Toast.makeText(this , msg , Toast.LENGTH_SHORT).show();
}
}
3 . 仿AsyncTask
刚才说过AsyncTask其内部是对线程和Handler的封装,简化我们的使用。现在我们自己来实现一个AsyncTask,以便更好的了解其内部实现原理。
AsyncTask在使用过程中常用的那几个方法执行顺序以及是否执行在UI线程中我们已经做了了解,我们知道在调用execute()的时候开启异步任务,首先调用的就是onPreExecute()方法,然后在子线程中执行doInBackground()方法,在执行过程中调用publishProgress()方法将执行进度发送到主线程中,通过onProgressUpdate()方法来更新UI;在doInBackground()方法执行完毕以后,执行onPostExecute()方法,将执行结果作为参数传递给onPostExecute()。这就是AsyncTask执行流程,下面就是我们自己实现的MyAsyncTask
public abstract class MyAsyncTask<Params , Progress , Result> {
private static InteranlHandler interHandler ; // 内部封装Handler用来发送消息,在主线程中更新UI
private static final int MSG_PROGRESS = 0 ; // 发送更新UI进度类型的类型
private static final int MSG_RESULT = 1 ; // 发送执行结果的消息类型
public MyAsyncTask(){
interHandler = new InteranlHandler(); // 在创建实例时,内部创建Handler对象
}
/**
* 调用execute方法,触发异步任务执行
* @param params
*/
public void execute(final Params... params){
onPreExecute(); // 首先调用onProExecute()方法
new Thread(){
@Override
public void run() {
Result result = doInBackground(params); // 在子线程中执行doInBackground方法,将获取的结果通过finish(Result)方法发送到主线程中
finish(result);
}
}.start();
}
/**
*通过AsyncTaskResult将数据封装起来发送到主线程中
*/
private void finish(final Result result){
AsyncTaskResult<Result> taskResult = new AsyncTaskResult<Result>(MyAsyncTask.this , result);
Message msg = Message.obtain();
msg.what = MSG_RESULT ;
msg.obj = taskResult ;
interHandler.sendMessage(msg);
}
/**
* execute方法执行之前立即在mainTread中执行,
*/
protected void onPreExecute(){
}
/**
* 在子线程中执行
* @param paramses
* @return
*/
protected abstract Result doInBackground(Params... paramses);
/**
* 更新UI,在主线程中执行
* @param progress
*/
protected void onProgressUpdate(Progress... progress){
}
/**
* 调用该方法,将数据封装起来,发送到主线程中,触发onProgressUpdate方法在主线程中执行
* @param progress
*/
protected void publishProgress(final Progress... progress){
AsyncTaskResult<Progress> result = new AsyncTaskResult<Progress>(MyAsyncTask.this , progress);
Message msg = Message.obtain();
msg.what = MSG_PROGRESS ;
msg.obj = result ;
interHandler.sendMessage(msg);
}
/**
* doInBackground执行结束后,将结果作为参数传递给该方法,触发该方法在主线程中执行
* @param result
*/
protected void onPostExecute(Result result){
}
private static class InteranlHandler extends Handler{
public InteranlHandler(){
super(Looper.getMainLooper());
}
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what){
case MSG_PROGRESS : // 在Handler中接受消息,调用onProgressUpdate()方法更新进度
result.asyncTask.onProgressUpdate(result.mData);
break;
case MSG_RESULT : // 调用OnPostExecute()方法将将结果作为参数传递给OnPostExecute(Result)
result.asyncTask.onPostExecute(result.mData[0]);
break;
}
}
};
/**
* 对数据进行封装
* @param <Data>
*/
private static class AsyncTaskResult<Data>{
public MyAsyncTask asyncTask ;
public Data[] mData ;
public AsyncTaskResult(MyAsyncTask task , Data... datas){
this.asyncTask = task ;
this.mData = datas ;
}
}
}
这样就完成了我们自己的AsyncTask,使用方式和AsyncTask一样。
//创建实例,然后调用execute(Params... params)方法触发异步任务执行,
private MyAsyncTask<Void , Integer , String> myAsyncTask = new MyAsyncTask<Void, Integer, String>() {
@Override
protected void onPreExecute() {
showToast("MyAsyncTask 执行onPreExecute方法,在主线程中执行");
}
@Override
protected String doInBackground(Void... voids) {
for(int i=0;i<=100;i++){
SystemClock.sleep(500);
publishProgress(i);
}
return "MyAsycTask后台方法执行完毕";
}
@Override
protected void onProgressUpdate(Integer... progress) {
pb.setProgress(progress[0]);
}
@Override
protected void onPostExecute(String result) {
showToast(result);
}
};
4. AsyncTask源码解析
当我们完成了仿AsyncTask之后,我们对AsyncTask内部实现原理也就一目了然了。AsyncTask内部就是对线程池和Handler的封装,调用execute方法时,内部会调用executeOnExecutor()方法,该方法执行后,会先调用onPreExecute()方法;然后执行FutureTask任务,这个过程中doInBackground(Params… params)会被调用,如果我们在doInBackground方法中调用publishProgress(Progress… values)方法,内部会通过InternalHandler(继承Handler)实例来发送一条MESSAGE_POST_PROGRESS消息,更新进度,在InternalHandler中处理消息时,会调用onProgressUpdte(Progress… progress)方法,doInBackground()方法执行完毕后,会发送一条MESSAGE_POST_RESULT消息处理结果,InternalHandler在处理结果时,会调用onPostExecute()方法。