上一节《Android中线程Thread的使用》,我们介绍了Java中Thread的一些基本的使用,在Android中除了Thread外,扮演线程角色的还有:底层用到线程池的AsyncTask以及底层封装线程的HandlerThread和IntentService,等。
AsyncTask
定义
Android为了降低开发难度,提供了AsyncTask抽象泛型类,它是一种轻量级的异步任务类,它封装了线程池和Handler。通过直接继承它可以很方便地实现后台异步任务的执行和进度的回调更新UI。在Android1.6之前,多个AsyncTask是串行执行任务,Android1.6时开始采用线程池里处理并扫任务,但从Android3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用了一个线程来串行执行任务,但仍然可以通过AsyncTask的executeOnExecutor方法来并行执行任务。所以AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。还有,AsyncTask的类必须在主线程中加载,当然这个过程在Android4.1及以上版本中已经被系统自动完成,在Android5.0的源码中,可以查看ActivityThread的main方法,它会调用AsyncTask的init方法。
AsyncTask的参数
AsyncTask提供了Params、Progress和Result这三个泛型参数,它们分别表示:
Params 启动任务执行的输入参数的类型
Progress 后台任务的执行进度百分比的类型
Result 后台执行任务最终返回的结果类型
如果不需要传递具体参数,可以用Void来代替。
AsyncTask的方法
AsyncTask提供了onPreExecute、onProgressUpdate、onPostExecute和onCancelled四个重要的回调方法,它们分别用作:
onPreExecute()在主线程中执行,在异步任务执行之前的回调,一般用于做一些准备工作
onProgressUpdate(Progress…values)在主线程中执行,在异步任务执行中进度发生改变时的回调,一般用于做进度的更新
onPostExecute(Resultresult)在主线程中执行,在异步任务正常执行完后之后被回调,一般用于做界面完成的更新
onCancelled()在主线程中执行,在异步任务被取消后执行完后的回调,一般用于做界面取消的更新
除了四个回调方法之外,还有一个非常重要的方法doInBackground,它是异步的主体:
doInBackground(Params…params)在线程池中执行,它方法体的代码便是异步进行的,在过程中可通过publishProgress方法来更新任务的进度,publishProgress()方法调用后onProgressUpdate方法会被回调。还有在过程中,可以通过isCancelled方法来判断外部是否有取消停止异步任务。另外doInBackground方法最终返回便会回调onPostExecute方法,其返回结果便是onPostExecute方法的参数。
开始和结束异步任务方法是execute和cancel:
execute(Params...params)必须在主线程中调用,表示要开始异步任务。值得注意的是,一个异步对象,只能调用一次execute方法,否则会报异常。
cancel(boolean mayInterruptIfRunning)必须在主线程中调用,表示要停止异步任务,cancel方法被调用后,在doInBackground方法里可以通过isCancelled方法来判断是否取消停止当前异步任务。参数mayInterruptIfRunning为false的话,线程不会被中断,只有执行完毕或碰到isCancelled方法才会退出doInBackground方法,若值为true的话,那么执行这个任务的线程将会被试图中断,它等于在Thread线程中被调用了interrup方法,如果在异步任务刚好在sleep的话会引发InterruptedException异常。
使用示例
private class MyAsyncTask extends AsyncTask<String, Integer, Long> {
@Override
protected Long doInBackground(String... params) {
int count = params.length;
long total = 0;
for(int i = 0; i < count; i++) {
// 模拟耗时操作
try{
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
// 更新进度给onProgressUpdate
publishProgress((int) ((i / (float) count) * 100));
total ++;
// 是否停止异步任务
if(isCancelled()) {
break;
}
}
// 返回结果给onPostExecute
return total;
}
@Override
protected void onProgressUpdate(Integer... progress) {
// 这里做进度更新界面
}
@Override
protected void onPostExecute(Long result) {
// 这里做完成界面处理
}
@Override
protected void onCancelled() {
// 如果被停止,则做停止界面处理
}
}
// 开始任务
mMyAsyncTask = new MyAsyncTask();
mMyAsyncTask.execute("xx", "xx", "xx");
// 停止任务
mMyAsyncTask.cancel(true);
AsyncTask的使用注意
1、前面讲到,execute方法和cancel方法必须在主线程中调用,而且一个对象只能调用一次execute方法,否则会报异常;
2、回调方法onPreExecute、doInBackground、onPostExecute和onCancelled以及异步主体方法doInBackground不能直接调用;
3、AsyncTask对象必须在主线程中创建。
HandlerThread
定义
HandlerThread继承了Thread,它本质上就是具有消息循环的Thread,它内部建立了Looper,即除了拥有Thread功能外,还可能使用Handler。HandlerThread内部run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()方法开启消息循环,所以run方法是死循环的,因此明确不需要再使用HandlerThread时,应该通过它的quit或quitSafely方法来终止线程的执行。
使用示例
private HandlerThread myHandlerThread ;
private Handler handler ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myHandlerThread = new HandlerThread("handler_thread") ;
myHandlerThread.start();
// 给Handler指定myHandlerThread的Looper
handler =new Handler(myHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 这里接收Handler发来的消息,运行在handler_thread线程中
//TODO...
}
};
// 在主线程给Handler发送消息
handler.sendEmptyMessage(1) ;
newThread(new Runnable() {
@Override
public void run() {
// 在子线程给Handler发送数据
handler.sendEmptyMessage(2) ;
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
myHandlerThread.quit() ;
}
IntentService
IntentService是一种特殊的Service,它继承了Service并且它是一个抽象类,系统对其进行了封装使其可以更方便地执行后台任务,IntentService内部采用了HandlerThread来执行任务,当任务执行完毕后IntentService会自动退出。从任务的角度来看IntentService的作用很像一个后台线程,但是IntentService是一种服务,正因为IntentService是一种服务,所以它的优先级比单纯线程高很多,从而不容易被系统杀死从而可以尽量保证任务的执行。
使用实例
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
publicMyIntentService() {
super(TAG);
}
@Override
protected void onHandleIntent(Intent intent) {
String action = intent.getStringExtra("action");
try {
Thread.sleep(2000);
}
catch(InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "onHandleIntent被调用, action:" + action);
//TODO...
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy被调用");
super.onDestroy();
}
}
// 调用
Intent intent = new Intent(this, MyIntentService.class);
intent.putExtra("action","test_intentservice1");
startService(intent);
intent.putExtra("action","test_intentservice2");
startService(intent);
intent.putExtra("action","test_intentservice3");
startService(intent);
别忘记了,IntentService也是一种Service,所以必须还要在AndroidManifest.xml中声明
<manifest
……
<application
……
<service android:name=".MyIntentService"/>
</application>
</manifest>
使用注意
继承IntentService的类至少要实现两个方法:构造函数和抽象方法onHandleIntent。onHandleIntent方法的作用是从Intent参数中区分具体的任务并执行这些任务
每执行一个后台任务就必须启动一次IntentService,IntentService启动后收到消息会将Intent对象传递给onHandleIntent方法去处理,如果多个后台任务同时存在,这些后台任务会按照发起的顺序排队执行,待所有后台任务都执行后它内部会自己尝试停止服务。
上示例中运行会打印:
onHandleIntent被调用,action:test_intentservice1
onHandleIntent被调用,action:test_intentservice2
onHandleIntent被调用,action:test_intentservice3
onDestroy被调用