Android_广播

BroadcastReceiver

一,概述

使用场景:

1.同一app内部的组件之间的消息通信

2.同一app不同进程之间的消息通信

3.不同app之间的组件之间消息通信

4.Android系统与app之间的消息通信

实现原理:观察者模式,基于消息的发布/订阅事件模型。

实现流程(大致):

1.接受者通过Binder机制向AMS(Activity Manager Service)进行注册

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

3.AMS查找符合条件(IntentFilter/Permission等)的接受者,将广播发送到接受者响应的消息循环队列中

4.消息循环队列执行拿到此广播,回调接受者中的onReceive()方法

二:自定义广播

要继承BroadcastReceiver,实现onReceive(Context context,Intent intent).一般用于启动service等操作。

 1 public class ChangeTimeReceiver extends BroadcastReceiver {
 2 
 3     @Override
 4     public void onReceive(Context context, Intent intent) {
 5         if(DynamicService.open){
 6             DynamicService.isChange = true;
 7             context.startService(new Intent(context,DynamicService.class));
 8         }
 9     }
10 }

 

注册类型:静态注册 和 动态注册

1,静态注册

在AndroidManifest.xml中

 1         <receiver
 2             android:name="com.google.android.apps.analytics.AnalyticsReceiver"
 3             android:exported="true" >
 4             <intent-filter>
 5                 <action android:name="com.android.vending.INSTALL_REFERRER" />
 6             </intent-filter>
 7         </receiver>
 8         <!-- 修改时间广播 -->
 9         <receiver android:name="com.sf.app.service.ChangeTimeReceiver" >
10             <intent-filter>
11                 <action android:name="android.intent.action.TIME_SET" />
12             </intent-filter>
13         </receiver>
14         <!-- 开机广播 -->
15         <receiver android:name="com.sf.app.service.StartNotifyReceiver" >
16             <intent-filter>
17                 <action android:name="android.intent.action.BOOT_COMPLETED" />
18             </intent-filter>
19         </receiver>
20         <receiver
21             android:name="com.sf.app.service.ConnectionChangeReceiver"
22             android:label="NetworkConnection" >
23             <intent-filter>
24                 <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
25             </intent-filter>
26         </receiver>
27         <receiver android:name="com.sf.app.service.ScreenGuardReceiver" >
28             <intent-filter android:priority="90000" >
29                 <action android:name="android.intent.action.USER_PRESENT" />
30                 <action android:name="com.sf.app.activity.getofflinemessage" />
31             </intent-filter>
32         </receiver>

 

android:name=""--Required name of the class implementing the receiver, deriving from
           android.content.BroadcastReceiver. [string]. * Required.
android:permission=""--Specify a permission that a client is required to have in order to use the associated object. [string]
android:process=""--Specify a specific process that the associated code is to run in. [string]
android:exported=""--Flag indicating whether the given application component is available to other applications. [boolean]

  其默认值是由receiver中有无intent- filter决定的,如果有intent-filter,默认值为true,否则为false。(同样的,activity/service中的此属性默 认值一样遵循此规则)同时,需要注意的是,这个值的设定是以application或者application user id为界的,而非进程为界(一个应用中可能含有多个进程);

android:singleUser=""--If set to true, a single instance of this component will run for all users. [boolean]

2.动态注册:

 1 //register BroadcastReceiver
 2         //method 1
 3         receiver = new BroadcastReceiver() {
 4             @Override
 5             public void onReceive(Context context, Intent intent) {
 6                 System.out.println("尝试收听广播");
 7                 if(MyAction.equals(intent.getAction())){
 8                     System.out.println("收到 SF 广播");
 9                 }
10             }
11         };
12         IntentFilter filter = new IntentFilter(MyAction);
13         manager = LocalBroadcastManager.getInstance(this);
14         manager.registerReceiver(receiver, filter);
15         //method 2 register receiver in the AndroidManifest.xml
16 //        registerReceiver(receiver, filter);
17     }
18     @Override
19     protected void onDestroy() {
20         System.out.println("onDestroy");
21         manager.unregisterReceiver(receiver);
22         super.onDestroy();
23     }

 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中。当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。

三:发送广播及广播类型

1 Intent intent = new Intent();
2 intent.setAction(BROADCAST_ACTION);
3 intent.putExtra("name", "qqyumidi");
4 sendBroadcast(intent);
类型:
1.Normal Broadcast:普通广播
2.System Broadcast:系统广播
3.Ordered Broadcast:有序广播

  1>多个具当前已经注册且有效的BroadcastReceiver接收有 序广播时,是按照先后顺序接收的,先后顺序判定标准遵循为:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照 priority属性值从大到小排序,对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。

  2>先接收的BroadcastReceiver可以对此有序广播进行截 断,使后面的BroadcastReceiver不再接收到此广播,也可以对广播进行修改,使后面的BroadcastReceiver接收到广播后解析 得到错误的参数值。当然,一般情况下,不建议对有序广播进行此类操作,尤其是针对系统中的有序广播。

4.Local Broadcast:app内广播--Android v4兼容包中给出了封装好的LocalBroadcastManager类,用于统一处理App应用内的广播问题

四:安全性:

 由前文阐述可知,Android中的广播可以跨进程甚至跨App直接通信,且注册是exported对于有intent-filter的情况下默认值是true,由此将可能出现安全隐患如下:

  1.其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;

  2.其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。

无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:

  1.对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;

  2.在广播发送和接收时,都增加上相应的permission,用于权限验证;

  3.发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。

  App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播确实可能需要用到。同时,之所以使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。

  相比于全局广播,App应用内广播优势体现在:

  1.安全性更高;2.更加高效。

五:其它

 

1.不同注册方式的广播接收器回调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是接收不到的)。

2.以下内容不一定正确。测试的时候有异常。

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

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


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


FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包

主要原因如下:

自Android3.1开始,系统本身则增加了 对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值为 FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的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是重新新建的。

1 Intent intent = new Intent();
2 intent.setAction(BROADCAST_ACTION);
3 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
4 intent.putExtra("name", "qqyumidi");
5 sendBroadcast(intent);

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

 注2:在3.1以前,相信不少app可能通过静态注册方式监听各种系统广播,以此 进行一些业务上的处理(如即时app已经退出,仍然能接收到,可以启动service等..),3.1后,静态注册接受广播方式的改变,将直接导致此类方 案不再可行。于是,通过将Service与App本身设置成不同的进程已经成为实现此类需求的可行替代方案。



 

posted @ 2015-03-02 15:21  王自强  阅读(434)  评论(0编辑  收藏  举报