后台线程如何正确发送消息(来自内网)

作者:xuding 转过来作为记录。

由于历史原因,我们有的APP依然使用webservice进行网络请求,我们为此封装了WebserviceUtils工具类,这个类具有明显的工具类特点——不与界面业务有任何耦合,这本应是个不错的设计。
然而我们长大后确发现,正因为不与界面耦合,WebserviceUtils在请求操作出错时,无法向界面传递消息,起初我们纠结万分,痛苦之中在研究了android Looper类的源码,于是我们自以为掌握了android消息队列的原理,参考:
http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html
于是有了如下代码:

01.try {

02. // 网络请求

03.} catch (Exception e) {

04. Log.e(TAG, “请求超时”, e);

05. Activity activity = ActivityStackManager.getCurrentActivity();

06. Looper.prepare();

07. SysUtils.closeHint();

08. Toast.makeText(activity, “网络通讯异常,请检查网络…”, Toast.LENGTH_LONG).show();

09. Looper.loop();

10.}
复制代码这样书写终于可以弹出提示了,然而悲剧的是,弹出提示紧接着,界面就失去响应(ANR),曾记否,那一年的冬天,大约也是这个季节,当时客户现场不断传来APP停止响应的呼吼,出错现象如同雪花在面前纷纷扬扬,而我们居然一片都抓不住啊
说正题,此处的问题很经典,需要从后台线程向界面上展示消息,Android展示消息只能在主线程里操作。Android中每个线程都有handler对象,消息框(Toast)、对话框等都是通过主线程的handler操作消息对列来做的。
上述代码中的Looper.prepare()、Looper.loop()是参考Handler.java源码来做的,那么为什么会ANR呢?
关键就是这里的loop(),和其他系统一样,android的消息队列也是通过死循环实现的:

01.for (;;) {

02. Message msg = queue.next(); // might block

03. if (msg == null) {

04. // No message indicates that the message queue is quitting.

05. return;

06. }

07. msg.target.dispatchMessage(msg);

08. // 。。。。。

09. msg.recycle();

10.}
复制代码低版本sdk中是while(){…),都一样,在后台线程loop()之后,因为这个死循环,该线程就一直处于阻塞状态了,于是这里的消息也就成了APP临终的遗言,紧接着ANR失去响应,然后就没有然后了。。。因为是死循环导致出错,所以APP捕获不到uncaughtException,也就无法输出错误日志了,于是更加让人不明所以,加大了排查错误的难度。
那么问题来了,后台线程究竟有没有办法向前台发消息呢?当然有,其实我们有了上述的知识,离胜利已经很近了:

01.new Handler(Looper.getMainLooper()).post(new Runnable() {

02. @Override

03. public void run() {

04. SysUtils.closeHint();

05. Toast.makeText(activity, “网络通讯异常,请检查网络…”, Toast.LENGTH_LONG).show();

06. }

07.});
复制代码非常优雅的通过Looper.getMainLooper()得到主线程handler,然后post!
非常简单吧!这个故事告诉我们一个道理,凡事一要求甚解,知其然还要知其所以然。我们有时候闭门造车容易将自己蒙蔽,吾尝终日而思,不如须臾之所学也。当我们脚踏实地,蓦然回首,就会发现,原来希望一直都在我们身边,只是我们被雪花迷失了方向。。。
万幸的是,这样的错误,我们以后的APP开发中很难再出现了,我们使用的annotations框架可以很优雅的处理各种对话框,从此Looper.loop()、AnsyncTask、Handler都成为了过往的种种。。。
其实Annotations处理主线程事件,也是通过上面的new Handler(Looper.getMainLooper()).post(…)来做的,欲知Annotations如何,且听下回分解~

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据