• 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本身设置成不同的进程已经成为实现此类需求的可行替代方案。



  • 相关阅读:
    Java获取EXE文件图标的方法
    项目组:ouc海票票 第七周Scrum meeting会议记录
    NBear使用的注意点
    几个错误
    DataGrid列中如何去掉小数后的零
    自己看着视频的理解:设计模式之abstractfactory模式(2)
    SQL SERVER 与ACCESS、EXCEL之间的数据转换(转载)
    .NET 将文本转换成语音 (转)
    InstallShield 打包需要的几段脚本代码
    SQL2005性能分析一些细节功能你是否有用到? (转)
  • 原文地址:https://www.cnblogs.com/wangziqiang/p/4308867.html
Copyright © 2020-2023  润新知