更新时间 | 修改意见 |
---|---|
2016-08-02 | 陈敏 |
多个线程之间除了有“执行的同步”关系,还有“数据的共享”关系,以及“功能的委托”关系。
例如之前提到的视频信息显示到列表上,
为此,Android SDK提供了Handler
帮助各个不同的线程之间传递数据或委托功能处理。
一个线程创建并start以后,就会开始执行它Runnable
中的run()
方法。如果要这个线程一直运行而不退出的话,就要在里面设置一个循环,
Runnable runnable = new Runnable() {
@Override
public void run() {
//开始循环
while(xxx)
{
}
}
};
Looper
可以为Thread
创建这样一个循环的环境,
@Override
public void run() {
......
Looper.prepare();
......
//相当于while()循环
Looper.loop();
......
}
Looper
具有一个消息队列,可以存放任何线程(包括它自己)给自己布置的任务。这些任务被一条一条的放在队列当中,在loop()
函数中被取出来-执行,然后又取出来-执行,周而复始,永不停止。
public static void loop() {
......
for (;;) {
//从消息队列queue中取出任务
Message msg = queue.next();
//用消息提供的方法来处理每个消息蕴含的任务
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
Handler
是和Looper
相关联的,通过Handler
,任何线程可以把需要完成的任务放到Looper
的消息队列里面。
Thread
就好比一条产线,Looper
中的消息队列就是这个流水线上的传送带,带子上分成了很多格,每一格放要处理的原料和处理这些原料的工人(原料和工人打包成了一个Message
)。等轮到格子上的工人时,工人才能开始处理格子里放的原料。Handler
就像是一个转运工具,提供给别的模块使用。这个工具可以把原料和工人放到产线的传送带上。
注意,一条产线只有一条传送带(Looper
);但可以有多个为同一条产线提供转运服务的转运工具(Handler
)。
所以使用这种产线的流程是,
Handler
最常见的使用场景是:
为了实现这样的功能,我们首先需要知道,
Activity
都是运行在程序的主线程当中的;Looper
,主线程已经有了消息处理的队列(生产流水线和流水线上的格子);用Handler
处理这种场景时,我们有2种方式。
Handler
;如果创建时直接new Handler()
,说明这个Handler
是放在主线程的消息队列中的“工人”;Handler
的handleMessage()
函数;handleMessage()
函数中,根据msg.what
的不同,进行对应的处理;//创建的Handler是挂在主线程上的
private Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message msg) {
switch(msg.what)
{
case MSG_XXX:
{
//这里是在主线程中执行的,可以修改界面元素了
}
break;
case ......
break;
default:
super.handleMessage(msg);
}
}
};
任何想要布置任务的线程只需要利用Handler
的sendMessage()
函数,就能把任务放到主线程的消息队列中,
Runnable runnable = new Runnable() {
@Override
public void run() {
//耗时的操作
while(!stop)
{
Message msg = mHandler.obtainMessage(MSG_XXX);
//用主线程的Handler向主线程布置任务
mHandler.sendMessage(msg);
}
}
};
Thread work = new Thread(runnable);
work.start();
这样主线程的消息队列中就有了这个新任务。等到这个消息被处理的时候,主线就可以根据参数修改界面元素了。
除了使用
Message msg = mHandler.obtainMessage(MSG_XXX);
mHandler.sendMessage(msg);
也可以使用
mHandler.obtainMessage(MSG_XXX).sendToTarget();
这里使用的Message
就携带了“工人”和“原料”,Message
通过Handler
对象获取,
//只设置Message的what数值
Message msg = mHandler.obtainMessage(what);
//设置Message的what数值和Object值
Message msg = mHandler.obtainMessage(what, object);
//设置Message的what数值,arg1、arg2和Object值
Message msg = mHandler.obtainMessage(what, arg1, arg2, object);
发送Message
可以直接发送,也可以延时发送,
//直接发送消息,将任务布置到消息队列中
mHandler.sendMessage(msg);
//延迟1000毫秒发送消息
mHandler.sendDelayedMessage(msg,1000)
创建一个能向主线程消息队列中布置任务的Handler
;如果创建时直接new Handler()
,说明这个Handler
是放在主线程的消息队列中的工人
;
Handler mHandler = new Handler();
在工作线程中使用Handler
的post()
方法,里面的Runnable
就是在Handler所服务线程中运行;
Runnable mHandlerRunnable;
Runnable runnable = new Runnable() {
@Override
public void run() {
//耗时的操作
while(!stop)
{
mHandlerRunnable = new Runnable() {
@Override
public void run() {
//这里是在主线程中执行的,可以修改界面元素了
}
};
//用主线程的Handler向主线程发送任务
mHandler.post(mHandlerRunnable);
}
}
};
Thread work = new Thread(runnable);
work.start();
还可以使用
//延迟1000毫秒执行runnable
mHandler.postDelayed(mHandlerRunnable, 1000);
大多数情况下,当Activity退出以后,需要将它布置给主线程的任务给移除掉。如果不移除,可能会遇到大麻烦。可以想象一下,
Handler
给主线程布置了一个任务-根据一个参数修改界面显示,此时这个任务已经放到了主线程的任务队列里面;这时,程序的行为表现有可能和你希望的不同,出现一些与期望不符的现象,严重时,可能会造成程序崩溃。所以当一个Activity退出销毁的时候,一定要把相关的任务移除。这样做还有助于避免内存泄露。
通过它移除任务队列中所有特定Message
,
mHandler.removeMessages(MSG_XXX);
通过它移除任务队列中所有特定Runnable
,
mHandler.removeCallbacks(mHandlerRunnable);
这里的mHandlerRunnable
就是之前post()
方式使用的mHandlerRunnable
,所以在使用post()
方式的时候,我们要把Runnable
的引用保存起来,以便以后的移除操作。
/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。
*另外,我们还推出了Arduino智能硬件相关的教程,您可以在我们的网店跟我学Arduino编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。
*最后再次感谢各位读者对安豆
的支持,谢谢:)
/*******************************************************************/