更新时间 | 修改意见 |
---|---|
2016-08-02 | 陈敏 |
第4节 HandlerThread
3.1章节介绍了Thread、Looper与Handler的关系。为了让开发者能简单的使用具备Looper
的Thread
,而不需要开发者写代码组合它们,Android SDK提供了HandlerThread
。
HandlerThread
的原理很简单,
- 创建一个线程A;
- 在A上创建一个
Looper
;
这样这个新创建的线程就可以不停的接收和处理任务了。
4.1 HandlerThread的使用
创建并启动
HandlerThread
;HandlerThread mHandlerThread = new HandlerThread("创建的线程名字"); mHThread.start();
获取可以访问
HandlerThread
对象任务队列的Handler
;把Thread的Looper
指定给Handler
;Handler mHandler = new Handler(mHandlerThread.getLooper());
使用
Handler
向HandlerThread
对象布置任务;mHandler.sendMessage(MSG_XXX);
让
HandlerThread
对象退出;//尽可能快的退出线程,对于队列中未处理的任务不再处理, //而对于正在处理的任务不会被马上中断,处理完该任务后再退出线程。 mHandlerThread.quit(); //执行完队列中的任务后退出线程 mHandlerThread.quitSafely();
4.2 HandlerThread与Handler、Thread名称区分
HandlerThread与Handler、Thread名字相似,这里再做一下澄清。
- Thread:线程,单纯的一个线程;
- HandlerThread:具备任务队列的线程,它就是一个线程;
- Handler:可以访问指定线程任务队列的一个工具;
/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。
/*******************************************************************/
第5节 AsyncTask
首先举个例子,获取手机上视频信息所需要的时间是个不能确定的事情。如果视频很少,也许几十毫秒就能完成,如果视频很多(比如几十个),也许就要花二十多秒。
安卓应用只有一个主线程-各个组件都是在这个线程中运行。作为组件的之一的Activity就是在这个线程中更新应用界面的,例如,用户点击界面上的一个按钮,按钮得到响应,整个过程就是在这个主线程里。所以这个主线程绝对不可以做耗时的操作。假如在按钮中做了耗时的操作,那么当它进行耗时操作的时候,你去点击界面上的其它按钮是不会有反应的,就好像程序冻在了那里。
代码的执行一旦连续占用这个线程超过一定的时间,系统就会弹出“程序无响应的”提示。
因此,我们可以考虑把获取视频信息的操作放到一个单独的线程thread中进行。
这就好比你在正在做一件事情A,突然另一件事情B来打扰你,你不得不停下手头的工作来完成,做完了才能继续之前的工作;这时如果有另外一个人(另一个线程)来帮助你,把事情B全部包揽了,那你就不用分心了。当另一个人把事情B做完后,告诉你一声就可以了。
5.1 异步操作
启动一个新的线程,分担耗时工作的方法是一种异步操作:我让你帮我做一件事情,布置任务后,我就去做其他的事情了,等你做完了再告诉我结果;
与它对应的是同步操作:我让你帮我做一件事情,布置任务后,我啥也不做,就等着你做完了告诉我结果;
获取视频信息是个异步操作,启动一个新线程-工作线程thread-查询视频信息,查询完成后,工作线程再将结果通知到主线程,让主线程将查询到的结果显示到界面上。界面的更新一定要在主线程中进行,不能在别的线程修改,否则系统会提示运行错误,这一点相当重要。因此我们一定要将查询的结果发送给主线程,让主线程处理界面的更新。
5.2 异步操作的方案
安卓系统提供的异步操作方案有:
- 创建工作线程thread和Handler,利用Handler在工作线程和主线程之间传递数据;
- 使用AsyncTask帮助类,AsyncTask中封装了工作线程,通过AsyncTask完成工作线程和主线程之间的数据传递;
第1种方案,已经在第3节里面介绍过了。
这里我们介绍第2种方案。
AsyncTask实际上它也是通过方案1
实现的,只不过Android SDK对这套机制做了进一步封装,让使用者用起来更加方便。
AsyncTask适用于,
- 使用场景简单,只是单个任务的异步操作,没有多个线程之间的数据同步考虑;
- 使用方便,不用考虑太多的新线程创建的细节;
5.3 AsyncTask的使用
AsyncTask需要被继承成为一个新的子类来使用,在被继承时,要指定三种参数的类型-Param
Progress
Result
,还需要实现doInBackground(Param...)
函数,此外通常还要实现onProgressUpdate(Progress...)
onPostExecute(Result)
两个回调函数。
class MyTask extends AsyncTask<Param, Progress, Result> {
//工作线程中运行
@Override
protected Result doInBackground(Param... params) {
return result;
}
//主线程中运行
@Override
protected void onProgressUpdate(Progress... progresses) {
}
//主线程中运行
@Override
protected void onPreExecute() {
}
//主线程中运行
@Override
protected void onPostExecute(Result result) {
}
//主线程中运行
@Override
protected void onCancelled() {
}
}
doInBackground(Param... params)
函数:传入参数的Param
类型就是AsyncTask<Param, Progress, Result>
中指定的Param
类型。它运行在新创建的工作线程当中。使用
MyTask
时,要在主线程中使用excute()
方法传入不定长参数,让Task
运行起来,MyTask task = new MyTask(); task.excute(param0, param1, ..., paramN);
不定长参数会以数组的形式传递到
doInBackground()
函数当中,@Override protected Result doInBackground(Param... params) { Param param0 = params[0]; Param param1 = params[1]; ...... Param paramN = params[N]; return result; }
onProgressUpdate(Progress... progresses)
函数:传入参数的Progress
类型就是AsyncTask<Param, Progress, Result>
中指定的Progress
类型。它运行在主线程中。在
doInBackground()
中执行的是一个很耗时的工作,有时需要向主线程报告当前的运行状况,这就要使用到publishProgress()
函数,publishProgress()
也是使用的不定长参数,@Override protected Result doInBackground(Param... params) { ...... publishProgress(progress1, progress2, ..., progressN) return result; }
不定长参数会以数组的形式传递到
onProgressUpdate()
函数当中,@Override protected void onProgressUpdate(Progress... progresses) { Progress progress0 = progresses[0]; Progress progress1 = progresses[1]; ...... Progress progressN = progresses[N]; }
onPreExecute()
函数:当Task
开始执行的时候,通过这里告诉主线程Task
要开始工作了。它运行在主线程当中。@Override protected void onPreExecute() { }
onPostExecute(Result result)
函数:传入参数的Result
类型就是AsyncTask<Param, Progress, Result>
中指定的Result
类型。它运行在主线程当中。doInBackground()
函数返回的类型也是Result
,@Override protected Result doInBackground(Param... params) { ...... return result; }
返回的结果作为参数传递给
onPostExecute()
函数,@Override protected void onPostExecute(Result result) { }
onCancel()
函数会在调用者取消AsyncTask
的工作的时候被触发。它运行在主线程当中。要取消
AsyncTask
的工作,首先要在主线程中调用cancel()
方法,task.cancel(true);
因为在
doInBackground()
中执行的是一个很耗时的工作,需要时不时的检查自己是否被取消执行了,@Override protected Result doInBackground(Param... params) { ...... if(isCancelled()) { ...... return result; } ...... return result; }
最后,
onCancelled()
函数会被触发,这个函数会在主线程中被执行,@Override protected void onCancelled() { }
一个AsyncTask只能被
excute()
一次,一旦运行之后,就不能重复的对它再次调用excute()
了。
综合上面的分析,自定义一个AsyncTask
的方法如下,
class MyTask extends AsyncTask<Param, Progress, Result> {
@Override
protected Result doInBackground(Param... params) {
Param param0 = params[0];
Param param1 = params[1];
......
Param paramN = params[N];
while(!isCancelled())
{
......
publishProgress(progress0, progress1, ..., progressN);
}
return result;
}
@Override
protected void onProgressUpdate(Progress... progresses) {
Progress progress0 = progresses[0];
Progress progress1 = progresses[1];
......
Progress progressN = progresses[N];
......
}
@Override
protected void onPreExecute() {
}
@Override
protected void onPostExecute(Result result) {
}
@Override
protected void onCancelled() {
}
}
使用一个AsyncTask
的方法如下,
MyTask task = new MyTask();
task.excute(param0, param1, ..., paramN);
......
task.cancel(true);
/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。
*另外,我们还推出了Arduino智能硬件相关的教程,您可以在我们的网店跟我学Arduino编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。
*最后再次感谢各位读者对安豆
的支持,谢谢:)
/*******************************************************************/