为什么要有异步线程?
因为有些操作是耗时的,比如大量计算啊,网络下载啊等,并且这些耗时操作的结果是需要反应在UI组件上的,比如进度条,计算结果等,如果这些耗时操作放在主线程里,就是阻碍主线程,从而导致用户交互被阻碍,出现假死,甚至ANR(Application not respond)。由此,异步线程的作用就是专门用户处理此类耗时操作,同时不阻碍主线程交互,当耗时操作过程中或者结束后,将结果反应在组件上。
Android上实现异步线程机制有两种方式:Handler 和 AsyncTask。
Handler模式是给每一个人物创建一个新的线程(Thread),任务完成后通过Handler实例向UI主线程发送消息,更新相关组件,完成界面的更新,这种方式比较精细,但是代码会相对比较臃肿,并且如果多个线程任务同时进行,无法对线程进行精确控制。
AsyncTask是Android为了更加方便地创建异步线程任务而提供的新的工具类,从Android1.5开始就有android.os.AsyncTask,实质上AsyncTask也是对Handler 和 Thread的更优秀的封装。
首先介绍一下AsyncTask的用法。
先来看看AsyncTask的定义:
Public abstract class AsyncTask<泛型1,泛型2,泛型3>{}
三种泛型类型分别代表“启动任务执行时需要的参数”,“后台任务执行过程中的进度”,“后台计算结果的类型”。有些场合并不需要参数,此时便可以使用java.lang.Void。
一个异步任务的执行一般包括以下几个步骤:
1. Execute(Params… params): 执行一个异步任务,需要我们再代码中调用此方法,启动异步任务;
2. onPreExecute() : 在execute被调用后则执行,实际它是存在于execute函数中,一般用来在执行后台耗时操作前,对即将更新的UI组件做一些标记;
3.doInBackGround(Params… params) : 调用Execute后,执行完OnPreExecute后,则开始执行耗时操作,此方法接收的是输入参数和返回计算结果,可以在此方法中实时更新计算进度,调用publishProgress(Progress… values);
4. onProgressUpdate(Progress… values) :在调用publishProgress(Progress… values)时执行,直接可以进行UI组件的更新;
5.onPostExecute(Result result) : 当后台操作结束后,此方法被调用,计算结果将作为参数传递到此方法中,直接将计算结果(即doInBackGround 函数返回结果)显示在UI组件上。
注意点:
1. AsyncTask的实例必须在主线程中创建;
2. 实例的执行execute函数必须在主线程中调用;
3. 一个AsyncTask的实例只能执行一次,否则会抛出异常;
接下来是一个简单地示例,每隔0.5s将进度增加1,当完成100%后,显示完成:
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
private static final String TAG = "ASYNC_TASK";
private Button execute;
private Button cancel;
private ProgressBar progressBar;
private TextView textView;
private MyTask mTask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
execute = (Button) findViewById(R.id.execute);
execute.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常
mTask = new MyTask();
mTask.execute("http://www.baidu.com");
execute.setEnabled(false);
cancel.setEnabled(true);
}
});
cancel = (Button) findViewById(R.id.cancel);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//取消一个正在执行的任务,onCancelled方法将会被调用
mTask.cancel(true);
}
});
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
textView = (TextView) findViewById(R.id.text_view);
}
private class MyTask extends AsyncTask<String, Integer, String> {
//onPreExecute方法用于在执行后台任务前做一些UI操作
@Override
protected void onPreExecute() {
Log.i(TAG, "onPreExecute() called");
textView.setText("loading...");
}
int progress = 0;
//doInBackground方法内部执行后台任务,不可在此方法内修改UI
@Override
protected String doInBackground(String... params) {
Log.e(TAG, "" + progress);
while(progress < 100){
publishProgress(progress++);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return "finished";
}
//onProgressUpdate方法用于更新进度信息
@Override
protected void onProgressUpdate(Integer... progresses) {
Log.i(TAG, "onProgressUpdate(Progress... progresses) called");
progressBar.setProgress(progresses[0]);
textView.setText("loading..." + progresses[0] + "%");
}
//onPostExecute方法用于在执行完后台任务后更新UI,显示结果
@Override
protected void onPostExecute(String result) {
Log.i(TAG, "onPostExecute(Result result) called");
textView.setText(result);
execute.setEnabled(true);
cancel.setEnabled(false);
}
//onCancelled方法用于在取消执行中的任务时更改UI
@Override
protected void onCancelled() {
Log.i(TAG, "onCancelled() called");
textView.setText("cancelled");
progressBar.setProgress(0);
execute.setEnabled(true);
cancel.setEnabled(false);
}
}
}
Xml布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/execute"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="execute"/>
<Button
android:id="@+id/cancel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:text="cancel"/>
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:progress="0"
android:max="100"
style="?android:attr/progressBarStyleHorizontal"/>
</LinearLayout>
注意加上网络访问权限:
<uses-permission android:name="android.permission.INTERNET"/>
软件运行界面如下:
AsyncTask的使用已经比较清晰,使用起来也十分方便,下面就要详细看一下AsyncTask究竟是个什么鬼。
AsyncTask源码片段分析。
从上图中可以看出,Asynctask中包含一个Status的枚举类,该枚举类中包含:PENDING(未执行),RUNNING(执行中),FINISHED(已完成)。
首先看一下AsyncTask中的成员变量:
private static final int CORE_POOL_SIZE =5;//5个核心工作线程
private static final int MAXIMUM_POOL_SIZE = 128;//最多128个工作线程
private static final int KEEP_ALIVE = 1;//空闲线程的超时时间为1秒
private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);//等待队列
private static final ThreadPoolExecutorsExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue,sThreadFactory);//线程池是静态变量,所有的异步任务都会放到这个线程池的工作线程内执行。
从execute(Params… params)入手,可以看到:
public final AsyncTask<Params,Progress, Result> execute(Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw newIllegalStateException("Cannot execute task:"
+ " the taskis already running.");
case FINISHED:
throw newIllegalStateException("Cannot execute task:"
+ " the taskhas already been executed "
+ "(a task canbe executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();//运行在ui线程,在提交任务到线程池之前执行
mWorker.mParams = params;
sExecutor.execute(mFuture);//提交任务到线程池
return this;
}
上半部分是根据status状态值做出相应反应,然后改变状态,调用onPreExecute()函数,但是后面的mWorker , sExecutor 以及 mFuture有点摸不着头脑。
当我们创建AsyncTask时,其构造函数则完成了mWorker和mFuture的实例:
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return doInBackground(mParams);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
Message message;
Result result = null;
try {
result = get();
} 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) {
message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
message.sendToTarget();//取消任务,发送MESSAGE_POST_CANCEL消息
return;
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(AsyncTask.this, result));//完成任务,发送MESSAGE_POST_RESULT消息并传递result对象
message.sendToTarget();
}
};
}
可以看出,在AsyncTask的默认构造函数中,完成了对mWorker 和 mFuture的定义。
mWorker是WorkerRunnable这个实现了Callable接口中的call() 方法的抽象内部类的实例,在其中调用doInBackground()函数:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
而mFuture是FutureTask的实例,而FutureTask是实现了RunnableFuture接口的,RunnableFuture接口又继承于Runnable 和 Future接口。其中FutureTask是一个可以中途取消的用于异步计算的类。 可以看到,mFuture的实例对象的done()方法中,如果捕捉到了CancellationException,就会发送一条”MESSAGE_POST_CANCEL”消息,当然,如果顺利进行,就会发送“MESSAGE_POST_RESULT” 消息 。这消息是怎么发送的呢?这些消息又是如何创建的呢?首先关于发送,我们可以看到,这些消息都是与一个sHandler对象关联的,而这个sHandler对象是AsyncTask 内部类InternalHandler的实例,而InternalHandler正式继承于Handler:
private static final int MESSAGE_POST_RESULT = 0x1; //显示结果
private static final int MESSAGE_POST_PROGRESS = 0x2; //更新进度
private static final int MESSAGE_POST_CANCEL = 0x3; //取消任务
private static final InternalHandler sHandler = new InternalHandler();
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
//调用AsyncTask.finish方法
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//调用AsyncTask.onProgressUpdate方法
result.mTask.onProgressUpdate(result.mData);
break;
case MESSAGE_POST_CANCEL:
//调用AsyncTask.onCancelled方法
result.mTask.onCancelled();
break;
}
}
}
由此看出,可以根据消息类型做不同处理:任务完成,任务进度更新,任务取消三类。当任务完成时,即MESSAGE_POST_RESULT,会调用AsyncTask的finish()方法,其中finish()方法的定义如下:
private void finish(Result result) {
if (isCancelled()) result = null;
onPostExecute(result); //调用onPostExecute显示结果
mStatus = Status.FINISHED; //改变状态为FINISHED
}
原来在finish()方法中是调用onPostExecute(Result result),这也就才解释了,为什么doInBackground(Params… params)的执行结果会“自动“传到onPostExecute(Result… result)中!当然,如果传过来的消息是”MESSAGE_POST_CANCEL“,那么就会调用AsyncTask 的onCanncelled()方法取消任务;
然后我们看看,在mFuture中这些MESSAGE是如何创建的呢?说完了上面的sHandler为何物,下面看看 MESSAGE的创建过程:
//发送取消任务的消息
message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
message.sendToTarget();
//发送显示结果的消息
message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(AsyncTask.this, result));
message.sendToTarget();
这个AsyncTaskResult又是个啥?
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
原来,AsyncTaskResult是一个封装了一个AsyncTask的实例和某种类型的数据集!
在处理消息的时候,我们首先获取到AsyncTaskResult的实例:
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
然后再使用 :
//完成任务
result.mTask.finish(result.mData[0]);
//更新任务进度
result.mTask.onProgressUpdate(result.mData);
至此,我们稍微深入地理解了AsyncTask部分源码片段。