代码改变世界

Android BroadcastReceiver(一)

2013-04-15 11:36  java20130722  阅读(157)  评论(0编辑  收藏  举报

Android BroadcastReceiver


介绍: broadcastReceiver是android的四大组件之一,大部分的广播是系统发出来的。例如,屏幕关闭,电池电量不足等等。应用同样可以创建广播,例如:当下载完成的时候,要让其他的应用知道这个情况,需要用到broadcastreceiver,receiver没有界面,它可能会创建一个status bar notification通知用户。broadcastreceiver 只是会做一些非常小的工作,例如,它可以出发一个service工作。


基类的代码会收到sendBroadcast()发送过来的Intents.

如果不需要在应用之间发送广播,可以考虑使用LocalBroadcastManager代替下面介绍的方式。这种方式会有更好的性能,并且不用考虑不同应用之间的安全问题,因为其他的应用有可能可以接收这个广播。

可以用Context.registerReceiver()动态注册receiver或者是在AndroidManifest.xml 文件里通过<receiver>元素静态注册receiver。

注意:    如果在Activity.onResume() 里面注册一个receiver,那么必须在Activity.onPause() 方法里面注销这个receiver。不要在Activity.onSaveInstanceState() 方法里面注销receiver,因为当用户回到历史堆栈中不会调用它。

这里有两种主要的广播可以收到:

  • 正常的 broadcasts ( Context.sendBroadcast发送的) 都是异步的. 所有的receiver都是没有顺序的,通常在同一时间。这种方式效率更好,这意味着receiver不能使用结果或者终止API。
  • 有序的 broadcasts (Context.sendOrderedBroadcast 发送的) 是同时发送给一个receiver。因为每一个receiver按照顺序执行,那么就可以传递结果到下一个receiver,或者它可以完全终止广播,已达到不传递给下一个receiver。有序的receiver运行的时与android:priority 有关系,这个属性可以控制它执行顺序;如果receiver拥有同样的priority,那么它们的执行顺序是任意的。

即使在正常的广播中,系统在有些情况下会把广播同时发给一个receiver。在特殊条件下,可能需要创建一个进程来处理receiver,在某一时间点只有一个receiver会运行,为了避免超负荷工作是系统可能创建新的进程。在这种情况下,然而,这些receiver还是不能够返回结果和终止他们的广播。

需要注意,即使Intent类被用来发送和接收广播,Intent 广播与启动activity的机制是完全不同的。BroadcastReceiver 不能处理startActivity()方法发送出来的Intent,没有这种机制;同样的,当广播一个Intent,同样不能找到或者启动一个activity。这两种操作在语义上是完全不同的:通过Intent启动一个activity是一个前台操作,这是随着用户的操作而发生的改变;广播一个Intent是一种后台操作,用户是不能意识到的.

下面有三个主题的内容:

  1. Security
  2. Receiver Lifecycle
  3. Process Lifecycle

Security

receiver通常是应用之间通信的一个工具,因此必须考虑其他的应用可能如何滥用它们。需要考虑的问题有:

  • Intent的命名空间是全局的。要保证定义的action名字和其他的字符串都是属于自己的应用,要不然会无意识的与系统其他的冲突。

  • 如果是用 registerReceiver(BroadcastReceiver, IntentFilter)注册Receiver, 任何应用都有可能发送广播到那个receiver。可以通过定义permissions控制谁能发送广播给它。

  • 在manifest里面定义receiver,并且定义了intent-filters, 任何应用都可以忽略指定的过滤条件并发送广播给它。为了防止其他的应用发送广播给它,在在manifest里面定义android:exported="false",这样其他的应用就不能发送广播到这个receiver了。

  • sendBroadcast(Intent)   或者相关的方法,正常情况,其他的应用可以收到这个广播。需要通过定义permission控制可以接受这个广播的receiver。或者是,从 ICE_CREAM_SANDWICH 开始,可以同通过设置Intent.setPackage 来指定receiver

用 LocalBroadcastManager 就不会有这个些问题,从广播发出后,都不会出当前进程。

receiver和broadcast都可以设置访问权限。

为了在发送的时候执行permission,必须提供一个非空的permission参数给 sendBroadcast(Intent, String) 或者 sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle). 仅仅定义这个permission的receiver可以接收广播( AndroidManifest.xml 里面定义<uses-permission>)。

当收到执行权限时,在注册receiver的时候需要提供一个非空的permission参数--要么当调用registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler) 或者是在AndroidManifest.xml 文件里面定义一个静态的<receiver>. 只有授予了指定permission的广播才能够发送到receiver中。

Receiver Lifecycle

BroadcastReceiver 对象只有在调用onReceive(Context, Intent)方法是才有效. 一旦从这个方法里面返回,系统会认为这个对象已经完成并且不在处于活动状态。

这里是当实现 onReceive(Context, Intent) 时需要注意: 任何需要异步的操作是不可行的,因为需要从方法里面返回,但是方法又要处理异步的操作,这样就可以行了,这种情况下,系统会在异步操作完成之前可能会杀死BroadcastReceiver的进程。

特殊情况下,在BroadcastReceiver里面可能不会显示一个dialog或者绑定一个service。对于前者,用NotificationManager API代替,对后者,可以用 Context.startService() 发送命令给service。

Process Lifecycle

当前正在运行的BroadcastReceiver(运行在onReceive(Context, Intent)方法上)进程需要被认为是前台进程,并且会一直运行除非是系统的内存处于极限情况下(系统会回收内存)。

一旦从onReceive()返回,BroadcastReceiver不再是活动状态的,它运行的进程与其他运行的在它里面的应用组件一样重要,这是非常特殊并且非常重要的,因为那个进程只是为BroadcastReceiver服务,然后receiver从onReceive()里面返回,系统会认为这个进程是空的并且会尽快杀死它回收资源。

这就意味着,如果是一个长时间运行的操作,最好是用service和BroadcastReceiver 结合使用,为了保持进程在整个操作中持久运行。