花样使用Handler与源码分析
前几天在跟公司大佬讨论一个问题时,看到他使用handler的一种方式,旁边的同事在说:以前不是这么用的啊。这个问题引发了我的好奇,虽然当时翻清楚道理了,但是还是想给大家分享一下。
handler在之前也说到过他的使用以及源码分析,而且相信大家都知道如何使用它,最常见的使用方法恐怕就是下面这种了:
handler handler = new handler(){ @override public void handlemessage(message msg) { super.handlemessage(msg); } };
这种情况会有一个问题:我们都知道handler是可以用在子线程给主线程更新的,当子线程给主线程回调时,主线程中的handler通过接收发送过来的对应消息,去执行对应的任务。而对于上面这个handler对象,如果他是主线程中的,那么我们子线程中需要拿到主线程的这个handler对象。
final handler handler = new handler(){ @override public void handlemessage(message msg) { super.handlemessage(msg); } }; new thread(new runnable() { @override public void run() { handler.sendmessage(new message()); } }).start();
但是上面这种写法实在是太不好看了,而且handler还是一个局部变量,在其他方法中也无法使用。
handler handler; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); log.e(tag, "oncreate: " ); handler = new handler(){ @override public void handlemessage(message msg) { super.handlemessage(msg); } }; new thread(new runnable() { @override public void run() { handler.sendmessage(new message()); } }).start(); }
这个看上去应该就好多了,可能也是大多数人的一种写法。
其实说白了,如果说我们要在子线程中给主线程/相应线程回调,那么一定要拿到主线程中的handler的索引。这么说就很直接了,可能有些情况下无法拿到主线程/相应线程的handler,或者拿到的方法很麻烦:
public class myservice extends service { @nullable @override public ibinder onbind(intent intent) { return null; } @override public void oncreate() { super.oncreate(); new thread(new runnable() { @override public void run() { //执行相关的耗时操作,然后结束后通过handler回调给主线程 } }).start(); } }
现在我有这样的需求:创建一个服务,在服务中开启一个子线程执行耗时操作,当执行完毕后回调在主线程中相应。这种情况下想要拿到主线程的handler对象也不是不可以,方法还是有很多,把主线程的handler写成static、创建类继承handler并且序列化,然后通过intent传入.....可能还有其他的一些方法,但是就目前的这些情况来看,貌似都不是很友好。下面给大家带来一种比较优雅且方便的方法:
public class myservice extends service { @nullable @override public ibinder onbind(intent intent) { return null; } @override public void oncreate() { super.oncreate(); new thread(new runnable() { @override public void run() { /*执行相关的耗时操作,然后结束后通过handler回调给主线程*/ new handler(looper.getmainlooper()).sendmessage(new message()); } }).start(); } }
只有一句话:又方便看着又舒服。
(可能有的朋友不知道looper是什么,本人之前写过一篇handler的文章对looper有所介绍,希望对大家有所帮助:)
通过looper.getmainlooper方法,可以获取到主线程的looper对象.
虽然之前说我们需要主线程中创建的handler,其实严格的说是不对的。究其根本是因为主线程已经为他自己加载了mainlooper,而我们在主线程中new handler,会默认获取主线程的looper引用。
public static void main(string[] args) { //pass looper.preparemainlooper(); //pass looper.loop(); throw new runtimeexception("main thread loop unexpectedly exited"); } public handler(callback callback, boolean async) { //pass mlooper = looper.mylooper();//在主线程中new的handler获取到的looper就是主线程的mainlooper if (mlooper == null) { throw new runtimeexception( "can't create handler inside thread that has not called looper.prepare()"); } //pass }
现在看来就很明确了,在主线程中创建handler只是个幌子,真正在背后操纵一切的其实是looper对象。所以只需要让handler的mlooper引用获取到主线程的引用就好了。
而且looper.getmainlooper方法是外部可见的,大胆猜测这个方法就是为了这种方便的写法而存在的。我们可以通过这个方法获取到主线程的looper,让他实现主线程中接收回调。
public handler(looper looper, callback callback, boolean async) { mlooper = looper; mqueue = looper.mqueue; mcallback = callback; masynchronous = async; }
但是注意我们上述的这种写法:new handler(....).sendmessage
这种写法不管你怎么去实现,他无法在主线程得到回应(是给大家挖了个坑哈哈),原因很简单:没有重写handler.handlermessage方法。
在使用handler接受消息时有三种方式:
- 重写handler.handlermessage方法,在该方法中接收
- 在handler构造器中实现callback接口,在回调接口中接收
- 不做任何处理,但是使用post方式发送消息。
在之前我们handler接收消息见到的几乎都是handlemessage方法,其实这只是其中一种方法,在执行该方法之前会有一个分发的方法dispatchmessage:
/** * handle system messages here. **/ public void dispatchmessage(message msg) { if (msg.callback != null) {//msg中的callback,这个是通过post方法自己封装的msg(自行查源码),优先级是最高的 handlecallback(msg); } else {//或者在构造器中实现handler的callback接口,这个优先级第二 if (mcallback != null) { if (mcallback.handlemessage(msg)) { return; } } handlemessage(msg);//这才是我们之前最常用的方法,最低的优先级 } }
可以看到在handlermessage方法之前还有两种回调的方法。在上述案例中我们并没有重写第三种方法,所以对于在子线程中匿名使用handler的情况,我们可以采取上述两种方案。代码就不写了,大家都是聪明人。
好了关于handler 的更多使用就到这里了,喜欢的朋友希望多多支持。有不同意见和理解的希望评论区多多交流。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。如果你想了解更多相关内容请查看下面相关链接
上一篇: C语言中,static关键字作用
下一篇: 基础知识 第一课
推荐阅读
-
Mysql Explain命令的使用与分析
-
在Linux中使用tcpdump命令捕获与分析数据包详解
-
[Abp vNext 源码分析] - 11. 用户的自定义参数与配置
-
spring源码分析系列5:ApplicationContext的初始化与Bean生命周期
-
spring源码分析6: ApplicationContext的初始化与BeanDefinition的搜集入库
-
浅要分析Python程序与C程序的结合使用
-
AJAX使用get与post模式的区别分析
-
PHP使用PDO 连接与连接管理操作实例分析
-
Python参数解析模块sys、getopt、argparse使用与对比分析
-
PHP迭代器和迭代的实现与使用方法分析