Android四大组件之广播

一、简要概述(基于5.0文档)

<receiver>

语法:
<receiver android:enabled=["true" | "false"]
          android:exported=["true" | "false"]
          android:icon="drawable resource"
          android:label="string resource"
          android:name="string"
          android:permission="string"
          android:process="string" >
    . . .
</receiver>
<application>的子元素,可以有<intent-filter> <meta-data>元素
描述:
Broadcast 可以接收系统发出的intent或者别的程序发出的intnet,即使之前Broadcast 没有运行也可以接收intnet。
有两种方式声明一个广播, 静态注册,使用<receiver>标签;动态注册,写个 BroadcastReceiver的子类,通过Context.registerReceiver()注册(别忘了Context.unregisterReceiver()
属性:
android:enabled
是否可以被系统实例化,默认是 " true".注意 <application>  里也有个enabled,两个同时为"true" 时,receiver才会起作用。
android:exported
是否可以接收到来自别的程序发来的消息, " true " 可以收到别的进程发出的消息,"false"只能接收自己程序或者同一个进程的消息。
该属性也有默认值,主要看receiver 有没有包含intent filters,如果没有任何意图,就只能被自己指定的类调用,意味着在程序内部使用。
该情况下默认值就是"false"。如果至少有一个intent filters,就说明可以接收系统或别的程序发出的消息。该情况下默认值就是"true"。
把receiver 暴露出去(接收其他进程的消息)的方式不仅仅是 "exported ",也可以使用permission  来限制。
android:icon       图标,没有的话就用<application> android:label      标签,没有的话就用<application> android:name
该属性一定要有,是继承自 BroadcastReceiver的一个子类,可以使用全限定类名。
android:permission
可以接收什么样的广播消息。如果没设置就看 <application>  里的 permission
android:process             receiver 运行的进程,通常,所有组件都运行在application以包名为进程名的进程中。             该属性允许组件运行在不同进程中,如果process以冒号“:”开头,当我们需要用到该组件时,该组件就会运行在一个私有进程中。      如果以小写字母开头,该组件就会运行在全局环境中,这样所有组件就会共享该组件,减少了资源的消耗。
BroadcastReceiver

如果你需要发送一个跨进程的广播,你可以考虑使用  LocalBroadcastManager ,这将会给你更有效的实现(不需要跨进程通信的),你就不需要考虑发出的消息会被别的程序接收到的安全问题。你可以动态注册也可以静态注册(上面说过了)。

Note:    If registering a receiver in your Activity.onResume() implementation, you should unregister it in Activity.onPause(). (You won't receive intents when paused, and this will cut down on unnecessary system overhead). Do not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back in the history stack.

这里有两个主要的广播类。

1)普通广播      

普通广播  是一个异步过程,在同一时间内,所有的接收者是没顺序的。接受者是不能中断拦截广播发送。

使用Context.sendBroadcast发送广播。

2)有序广播

当一个接收者接收到广播后,可以将广播发送给下一个。上一个接收者可以给下一个接收者传递一个result,或者上一个接收者完全可以拦截广播,这样下一个接收者就收不到广播了。接收者之间的接收顺序可以由android:priority指定,如果android:priority值是一样的,接收就随意。

将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。

          还有 粘性广播(在 android 5.0/api 21deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated),对此不再考虑粘性广播,请看官方给出的原因:

Open Declarationvoid android.content. Context.sendStickyBroadcast( Intent intent)

Deprecated. Sticky broadcasts should not be used. They provide no security (anyone can access them), no protection (anyone can modify them), and many other problems. The recommended pattern is to use a non-sticky broadcast to report that something has changed, with another mechanism for apps to retrieve the current value whenever desired.

Perform a sendBroadcast(Intent) that is "sticky," meaning the Intent you are sending stays around after the broadcast is complete, so that others can quickly retrieve that data through the return value ofregisterReceiver(BroadcastReceiver, IntentFilter). In all other ways, this behaves the same assendBroadcast(Intent).

You must hold the android.Manifest.permission.BROADCAST_STICKY permission in order to use this API. If you do not hold that permission,SecurityException will be thrown.



Open Declarationvoid android.content. Context.sendStickyOrderedBroadcast( Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)

Deprecated. Sticky broadcasts should not be used. They provide no security (anyone can access them), no protection (anyone can modify them), and many other problems. The recommended pattern is to use a non-sticky broadcast to report that something has changed, with another mechanism for apps to retrieve the current value whenever desired.

Version of sendStickyBroadcast that allows you to receive data back from the broadcast. This is accomplished by supplying your own BroadcastReceiver when calling, which will be treated as a final receiver at the end of the broadcast -- its BroadcastReceiver.onReceive method will be called with the result values collected from the other receivers. The broadcast will be serialized in the same way as callingsendOrderedBroadcast(Intent, String).

Like sendBroadcast(Intent), this method is asynchronous; it will return before resultReceiver.onReceive() is called. Note that the sticky data stored is only the data you initially supply to the broadcast, not the result of any changes made by the receivers.

See BroadcastReceiver for more information on Intent broadcasts.


在某些情况下,即使是普通广播,在同一时刻系统也只会返回一个可以传递的广播,特别是,需要创建进程的接收者,在同一时刻只有一个接收者可以运行,但是上一个接收者任然不能向下一个接收者传递result和拦截中断广播。

Security

接收者使用Context ,所以你必须考虑怎么才能不让别的程序滥用你发出的广播,有些事要考虑:
  • 保证intent里的 action名字不与其他程序冲突。

  • 当我们registerReceiver(BroadcastReceiver, IntentFilter)注册广播时,任何程序都可以向这个广播发消息,你可以使用如下方式控制谁可以发。

  • 当我们在manifest 里注册一个带有 intent-filters的广播时,不管filters 是不是你指定的,任何程序都可以向它发消息。为了阻止这样,你可以使用android:exported="false"

  • 当我们使用sendBroadcast(Intent) 或者相关的API时,通常任何程序都可以收到发出的广播,但是通过如下方式,你可以控制谁可以接收该条广播。从ICE_CREAM_SANDWICH(android4.0,简称ICS,冰淇淋三明治开始,你可以使用Intent.setPackage发一条限制广播。

使用LocalBroadcastManager以上问题都不存在,因为发出的广播只能在当前进程里。外面看不见。

Receiver Lifecycle

BroadcastReceiver 对象从onReceive(Context, Intent)执行开始到执行结束就是BroadcastReceiver 的生命周期。广播的生命是很短的,不要在里面做耗时操作和异步操作。一般情况下,根据实际业务需求,onReceive方法中都会涉及到与其他组件之间的交互,如发送Notification、启动service等。

注意:不要在BroadcastReceiver里显示一个dialog 或者绑定一个service ,对于前者,你可以使用NotificationManager ,后者可以使用 Context.startService()

Process Lifecycle

当执行BroadcastReceiver 的onReceive(Context, Intent)方法时(从开始到结束),recever所在的进程会被认为是前台进程。除非是系统内存不够时才会将其Kill,否则将一直运行。

一旦onReceive(Context, Intent)的方法返回时,BroadcastReceiver 的生命就结束了,所在的宿主进程就和其他组件一样。当用户长时间没有操作时,对于只有BroadcastReceiver 的进程,一旦onReceive()执行结束,系统就会认为这是个空进程,系统会随时kill它以保证资源。

所以用户在操作时,保证程序长时间运行可以使用Service 和BroadcastReceiver 。

Public Methods
final void abortBroadcast()
Sets the flag indicating that this receiver should abort the current broadcast; only works with broadcasts sent through  Context.sendOrderedBroadcast.
final void clearAbortBroadcast()
Clears the flag indicating that this receiver should abort the current broadcast.
final boolean getAbortBroadcast()
Returns the flag indicating whether or not this receiver should abort the current broadcast.
final boolean getDebugUnregister()
Return the last value given to  setDebugUnregister(boolean).
final int getResultCode()
Retrieve the current result code, as set by the previous receiver.
final  String getResultData()
Retrieve the current result data, as set by the previous receiver.
final  Bundle getResultExtras(boolean makeMap)
Retrieve the current result extra data, as set by the previous receiver.
final  BroadcastReceiver.PendingResult goAsync()
This can be called by an application in  onReceive(Context, Intent) to allow it to keep the broadcast active after returning from that function.
final boolean isInitialStickyBroadcast()
Returns true if the receiver is currently processing the initial value of a sticky broadcast -- that is, the value that was last broadcast and is currently held in the sticky cache, so this is not directly the result of a broadcast right now.
final boolean isOrderedBroadcast()
Returns true if the receiver is currently processing an ordered broadcast.
abstract void onReceive( Context context,  Intent intent)
This method is called when the BroadcastReceiver is receiving an Intent broadcast.
IBinder peekService( Context myContext,  Intent service)
Provide a binder to an already-running service.
final void setDebugUnregister(boolean debug)
Control inclusion of debugging help for mismatched calls to  Context.registerReceiver().
final void setOrderedHint(boolean isOrdered)
For internal use, sets the hint about whether this BroadcastReceiver is running in ordered mode.
final void setResult(int code,  String data,  Bundle extras)
Change all of the result data returned from this broadcasts; only works with broadcasts sent through Context.sendOrderedBroadcast.
final void setResultCode(int code)
Change the current result code of this broadcast; only works with broadcasts sent through Context.sendOrderedBroadcast.
final void setResultData( String data)
Change the current result data of this broadcast; only works with broadcasts sent through Context.sendOrderedBroadcast.
final void setResultExtras( Bundle extras)
Change the current result extras of this broadcast; only works with broadcasts sent through Context.sendOrderedBroadcast.

二、例子

1、内部广播

        //registerReceiver(mBroadcastReceiver, intentFilter);  //注册应用内广播接收器 
	localBroadcastManager = LocalBroadcastManager.getInstance(this); 
	localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);          
	//unregisterReceiver(mBroadcastReceiver);  //取消注册应用内广播接收器 
	localBroadcastManager.unregisterReceiver(mBroadcastReceiver); 
	Intent intent = new Intent();
	intent.setAction(BROADCAST_ACTION);
	intent.putExtra("name", "qqyumidi");
	//sendBroadcast(intent);
	//发送应用内广播
	localBroadcastManager.sendBroadcast(intent);

2、不同注册方式的广播接收器回调onReceive(context, intent)中的context具体类型

1).对于静态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是ReceiverRestrictedContext

2).对于全局广播的动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Activity Context

3).对于通过LocalBroadcastManager动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Application Context

注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)。


三、不同Android API版本中广播机制相关API重要变迁

1).Android5.0/API level 21开始粘滞广播和有序粘滞广播过期,以后不再建议使用;

2).静态注册的广播接收器即使app已经退出,主要有相应的广播发出,依然可以接收到,但此种描述自Android 3.1开始有可能不再成立“

Android 3.1开始系统在Intent与广播相关的flag增加了参数,分别是FLAG_INCLUDE_STOPPED_PACKAGESFLAG_EXCLUDE_STOPPED_PACKAGES

FLAG_INCLUDE_STOPPED_PACKAGES:包含已经停止的包(停止:即包所在的进程已经退出)

FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包

主要原因如下:

Android3.1开始,系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGESflag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播。

Android官方文档:

http://developer.android.com/about/versions/android-3.1.html#launchcontrols

由此,对于系统广播,由于是系统内部直接发出,无法更改此intent flag值,因此,3.1开始对于静态注册的接收系统广播的BroadcastReceiver,如果App进程已经退出,将不能接收到广播。

但是对于自定义的广播,可以通过复写此flag

FLAG_INCLUDE_STOPPED_PACKAGES,使得静态注册的BroadcastReceiver,即使所在App进程已经退出,也能能接收到广播,并会启动应用进程,但此时的BroadcastReceiver是重新新建的。

	  Intent intent = new Intent();
	  intent.setAction(BROADCAST_ACTION);
	  intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
	  intent.putExtra("name", "qqyumidi");
	  sendBroadcast(intent);


1:对于动态注册类型的BroadcastReceiver,由于此注册和取消注册实在其他组件(如Activity)中进行,因此,不受此改变影响。

2:在3.1以前,相信不少app可能通过静态注册方式监听各种系统广播,以此进行一些业务上的处理(如即时app已经退出,仍然能接收到,可以启动service..,

3.1后,静态注册接受广播方式的改变,将直接导致此类方案不再可行。于是,通过将ServiceApp本身设置成不同的进程已经成为实现此类需求的可行替代方案。

从实现原理看上,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。因此,从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。具体实现流程要点粗略概括如下:

1.广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;

2.广播发送者通过binder机制向AMS发送广播;

3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;

4.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。

 对于不同的广播类型,以及不同的BroadcastReceiver注册方式,具体实现上会有不同。但总体流程大致如上。

由此看来,广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心。广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。显然,整体流程与EventBus非常类似。




四、Android里的系统广播


1.  Intent.ACTION_BOOT_COMPLETED   开机完成

    android.intent.action.BOOT_COMPLETED   

需要android.permission.RECEIVE_BOOT_COMPLETED


2.ConnectivityManager.CONNECTIVITY_ACTION   网络变化

android.net.conn.CONNECTIVITY_CHANGE

需要android.permission.ACCESS_NETWORK_STATE


3.      WifiManager.WIFI_STATE_CHANGED_ACTION  

android.net.wifi.WIFI_STATE_CHANGED

需要android.permission.ACCESS_NETWORK_STATE






参考:http://www.cnblogs.com/lwbqqyumidi/p/4168017.html


















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值