Android面试一天一题(2 Day)

作者: goeasyway | 来源:发表于2016-05-17 14:31 被阅读8710次

面试题:用广播来更新UI界面好吗?

做为Android四大组件之一的,广播被很多人所熟知,可算是一种非常方便的解耦组件的手段。常用的方式是直接调用Context的接口(sendBroadcast & sendOrderBroadcast)发送两类型的广播:

Normal broadcasts无序广播,会异步的发送给所有的Receiver,接收到广播的顺序是不确定的,有可能是同时。
Ordered broadcasts有序广播,广播会先发送给优先级高(android:priority)的Receiver,而且这个Receiver有权决定是继续发送到下一个Receiver或者是直接终止广播。

当然,静态和动态的两种注册Receiver的方式也难不住面试者。只是有时为了看一下面试者是否真的全面了解广播,会问一下:

除了上面的两种广播外,还有其他类型的广播吗?

允许我心里小小的邪恶一下。很少有人知道这种方式的,可以使用sendStickyBroadcast发送Sticky类型的广播。Sticky简单说就是,在发送广播时Reciever还没有被注册,但它注册后还是可以收到在它之前发送的那条广播。

有时候基于数据安全考虑,我们想发送广播只有自己(本进程)能接收到,那么该如何去做呢?

在我不知道有新的API或者框架时我常常喜欢用自己现有的知识去想方案,最后再Google一下看是否有更好的。这个问题,我会先想到权限,发送广播时限定有权限(receiverPermission)的接收者才能收到。但是我们知道APK太容易被反编译,注册广播的权限也只是一个字符串,并不安全。

然后可能使用Handler,没错,往主线程的消息池(Message Queue)发送消息,只有主线程的Handler可以分发处理它,广播发送的内容是一个Intent对象,我们可以直接用Message封装一下,留一个和sendBroadcast一样的接口。在handleMessage时把Intent对象传递给已注册的Receiver。

后来在看项目组的其他同事写代码时,发现还有一个LocalBroadcastManager类,查了一下官方文档是Support V4包里的一个类,其实现方式也是使用Handler,思路也是一样的。

BroadcastReceiver的生命周期

有些人并不态清楚Receiver也是有生命周期的,而且很短,当它的onReceive方法执行完成后,它的生命周期就结束了。这时BroadcastReceiver已经不处于active状态,被系统杀掉的概率极高,也就是说如果你在onReceive去开线程进行异步操作或者打开Dialog都有可能在没达到你要的结果时进程就被系统杀掉。因为这个进程可能只有这个Receiver这个组件在运行,当Receiver也执行完后就是一个empty进程,是最容易被系统杀掉的。替代的方案是用Notificaiton或者Service(这种情况当然不能用bindService)。

回到今天的面试题,使用广播来更新界面是否合适?

更新界面也分很多种情况,如果不是频繁地刷新,使用广播来做也是可以的。但对于较频繁地刷新动作,建议还是不要使用这种方式。广播的发送和接收是有一定的代价的,它的传输是通过Binder进程间通信机制来实现的(细心人会发现Intent是实现了Parcelable接口的),那么系统定会为了广播能顺利传递做一些进程间通信的准备。

除此之外,还可能有其他的因素让广播发送和到达是不准时的(或者说接收是会延时)。曾经看到有人在论坛上抱怨发几个广播都卡,Google的工程师是怎么混饭吃的。

这种情况可能吗?很可能,而且很容易发生。我们要先了解Android的ActivityManagerService有一个专门的消息队列来接收发送出来的广播,sendBroadcast执行完后就立即返回,但这时发送来的广播只是被放入到队列,并不一定马上被处理。当处理到当前广播时,又会把这个广播分发给注册的广播接收分发器ReceiverDispatcher,ReceiverDispatcher最后又把广播交给接Receiver所在的线程的消息队列去处理(就是你熟悉的UI线程的Message Queue)。

整个过程从发送--ActivityManagerService--ReceiverDispatcher进行了两次Binder进程间通信,最后还要交到UI的消息队列,如果基中有一个消息的处理阻塞了UI,当然也会延迟你的onReceive的执行。

小结

在项目里还是有遇到开发骨干也会在onReceive中开线程做耗时操作,很多时候他们这样做了并不会立刻就会产生问题,但是并不等于不会产生问题,当设备达到特定的临界条件时(如内存紧张),这些问题往往会在最终用户那里报出来。我们都有经验由用户报出来的随机BUG往往难于跟踪和修复,所以理解清楚一些基础的机制是很有必要的,它们能帮助我们避免一些隐藏的风险。

相关文章

  • Android面试一天一题-goeasyway

    以下文章作者:goeasyway Android面试一天一题(1 Day) Android面试一天一题(2 Day...

  • MS(1):Android之架构篇

    一、架构相关 1、MVC,MVP,MVVM MS思考:Android面试一天一题(Day 33:Android开发...

  • 面试题

    最全的BAT大厂面试题整理答案Android面试一天一题(Day 37:一套高级工程师的面试题)Android面试...

  • MS(2):Android之基础知识篇

    二、组件 1、Activity----------1 MS思考:Android面试一天一题(3 Day):Acti...

  • MS(7):非技术问题篇

    一、GM问非技术问题汇总 MS思考:Android面试一天一题(Day 34:常去的Android相关站点) 说下...

  • MS(3):Android之机制原理篇

    五、重点机制原理 1、Handler机制 MS思考:Android面试一天一题(8 Day):Handler相关分...

  • MS(5):android之进阶篇

    七、自定义View MS思考:Android面试一天一题(Day 30:老外的自定义View面试题)MS思考:老外...

  • android Service 一些思考

    看了Android面试一天一题(1 Day),对Service 的一些总结与思考 作者原文章链接: http://...

  • MS(6):Java篇

    MS思考:Android面试一天一题(7 Day):Java相关 1.Switch能否用string做参数? 在J...

  • 读goeasyway文章有感

    今天在地铁上读goeasyway的文章《Android面试一天一题(Day 39:写博客很重要吗?)》,很有感触,...

网友评论

  • andriod小学徒:我做蓝牙项目时用,在这个方法里开子线程取数据的,有时候取不到数据
  • dongbingliu:发送广播的类型?
    1、sendBroadcast();2、sendOrderBroadcast();

    广播更新界面数据UI是否合适?
    1. 不推荐
    2.不稳定,会出现消息阻塞
  • PeterHe888:sendStickyBroadcast 发送的时候还没有注册,那是什么时候注册呢?官方现在好像不推介使用这个了。
  • 9b4f2400ea72:如果你在onReceive去开线程进行异步操作或者打开Dialog都有可能在没达到你要的结果时进程就被系统杀掉。因为这个进程可能只有这个Receiver这个组件在运行,当Receiver也执行完后就是一个empty进程,是最容易被系统杀掉的。
    这一段好像大家都这么说,但是我实测的时候,发现并不像文中这样。
    打开的子进程会一直存在,就好像service在destroy之后,并不会关闭它的子进程一样,如果要杀死这个进程,可能要长按home关闭进程或者等资源不够进程被强制关闭才可以。
    其次想要打开dialog,只能使用onReceiver的context,而这个context是applicationContext。首先需要悬浮窗权限才可以打开,其次打开之后生命周期和application是一样的。
    至于onReceive方法,和UI线程是一个线程,不存在新的进程,一个app应该只有一个进程。所以只要注意onReceive方法不能进行耗时操作以避免anr就行。
    goeasyway:虽然是空进程了,其实只是被系统回收的概率变高了,并不是100%被回收。
  • 77324d9b47bb:因为这个进程可能只有这个Receiver这个组件在运行,当Receiver也执行完后就是一个empty进程,是最容易被系统杀掉的。替代的方案是用Notificaiton或者Service(这种情况当然不能用bindService)。博主,我不明白这里为什么不能bindService啊?求指教
    快乐石头111:BIndService绑定方式特性是和宿主同生共死,所以如这样,等receiver结束了,service也会挂了
  • 望北8261:原来 onReceive 方法是运行在一个独立的进程中的么
    a0b93da7572c:@SuperLino // How long we allow a receiver to run before giving up on it.
    static final int BROADCAST_FG_TIMEOUT = 10*1000;
    static final int BROADCAST_BG_TIMEOUT = 60*1000;
    前台进程的广播 ANR是10秒,后台进程是60秒。
    可以看下ActivityManagerService源码
    SuperLino:onReceive方法运行在应用的主(UI)进程中的呀 阻塞运行超过5秒就ARN了
  • Vision_Zhao:很详细
  • DylanW:“在handleMessage时把Intent对象传递给已注册的Receiver。”这个怎么做到呢?谢谢
    goeasyway:@DylanW 直接看一下LocalBroadcastManager这个类的源代码会一下就明白了。
  • Skypew:不错,解惑了,感觉我还是没深入的想问题
  • 流穿枫:项目中直接没用到广播,需要用到广播的场景都被eventBus给取代了。
    Hilary_Lu: @流穿枫 因为没涉及进程间通信吧
  • 扑扑兔:看到了第二篇,为什么没给答案? :joy:
    用Messenger实现Service和Activity通信可好?应该比用广播省去了ReciverDispather这一步了
    天青色等煙雨_而我在等妳:@扑扑兔 用Messenger实现Service和Activity通信,怎么做啊?
  • yzytmac:大神!膜拜
  • i卓:请问一下,我这边有用一般广播收发消息,自定义权限限定收发者,您说这种写法不安全,那使用一般广播自定义权限的做法有何意义呢,这个做法根本不靠谱啊。。😂
  • 8314e3a0c30e:解决了我多年的疑惑:smiley:
    天青色等煙雨_而我在等妳:@赵丰年 什么疑惑啊?分享一下
  • HuDP:博主你好,还是没能理解往主线程的消息队列里面发送广播 这是什么意思? 如何实现的?看来有必要看看本地广播的源码了
  • 李文文丶:作者在回答"如果想广播只有本进程接受..."这个问题中,使用了handler来解决。(ps:我在第一行代码书里,里面就介绍了LocalBroadcastManager)而在更新ui的方法里,一般都是使用handler。那作者关于"用广播来更新ui真的好吗?"的问题,是不是可以理解成更新ui最好的是用handler么。。。我说的可能是错的,不要打我
    b0f758808ba4:@bramles adapter里面不是有个notifydatasetchanged方法吗
    573a725e1add:@goeasyway 大神 那在ListView中更新item应该用什么
    goeasyway:@李文文丶 并不是说更新最好用hanlder,可以想一下ListView的场景,如果ListView有的ITEM要展新进度条,用handler来更新话,效果很差。

本文标题:Android面试一天一题(2 Day)

本文链接:https://www.haomeiwen.com/subject/ltnbrttx.html