Android无序广播最近使用整理——基础代码、权限

来源:互联网 发布:淘宝店铺快递怎么设置 编辑:程序博客网 时间:2024/05/21 07:56
      四大组件中,广播接收器是相对比较容易理解的,和日常生活中的广播类似,有发送者和接受者,发送者不关心接受者具体干了什么,只负责把广播发出去,接收者也不关心谁发送了广播,只负责接收并处理,原理很好理解,但实际开发中需要特别注意一些问题,汇总如下:
     Q1  不借助参考代码及文档,写出无序广播接收器的基础代码
     Q2  注册方式的区别及如何注销全局广播接收器。
     Q3  发送方如何指定该广播可以被谁接收。
     Q4  接受方如何指定只接收谁发送的广播。
     Q5  本地接收器用法
     问题都很基础,但需要重视,在项目开发时,很多进度都被基础不牢耽误了~

Q1  基础代码:

      项目包名为com.klpchan.bcrtest1,normalBC是无序广播的子包名  
     发送端//自定义action,命名规则类似于包名,需确定Intent执行类型。public static final String ACTION_NORMAL_BC = "com.klpchan.bcrtest1.normalBC.VIEW";sendBroadcast(new Intent(ACTION_NORMAL_BC));       //基本函数     接收端//自定义接收器类,静态注册时需加上public修饰符              public class NormalBCReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stub     processReceicedBC();//处理函数,另行添加}}        //动态注册方式,在接收端Activity中        context.setBCReceiver();        private void setBCReceiver() {                IntentFilter intentFilter = new IntentFilter(ACTION_NORMAL_BC);                BroadCastReceiver normalBCReceiver = new NormalBCReceiver();                registerReceiver(normalBCReceiver, intentFilter);        }       //静态注册方式,AndroidManifest.xml中                   <receiver           android:name=".normalBC.NormalBCReceiver">           <intent-filter>               <action android:name="com.klpchan.bcrtest1.normalBC.VIEW" />           </intent-filter>               </receiver>
      代码写完了,运行验证结果,轻松加愉快,IDE确实强大,有兴趣的同学可以尝试在记事本里练习基础代码,能够加深对类关系的理解,无序广播的基础代码并不复杂,但是需要注意一下几点
       1)发送端在发送广播时,会通过设置intent对象的action字符串来表明此广播的意图和什么样的过滤器能够接受该广播。
       2)无论使用何种注册方式,均需通过自定义接收器类来确定处理函数流程。(sticky广播代码例外)
       3)两种注册方式均需定义可以接受广播的intent-action内容,且需和发送端定义的action内容一致。
       4)静态注册方式需要广播接收器开发权限为public,动态注册无此限制。

Q2 注册方式的区别及如何注销全局广播接收器。

       静态注册是写在项目的AndroidManifest.xml文件中,当注册有接收器的应用第一次运行时,系统会解析xml文件中的receiver并将其映射到一个全局性的共享内存区,此后无论这个应用是否运行,receiver始终是存在的且可以接到符合条件的广播,直到配置有receiver的应用被卸载。也就说静态注册适用的是全局性的、和具体activity及应用无关的,持续存在的情况,这些receive多半和service相关,典型应用场景是当系统启动时执行自定义操作及接受系统的广播消息等,静态注册的好处是一次注册,多次使用。不需要应用环境支持,对于系统广播可以随时接受并处理。
      动态注册恰好与之相反,通过在具体的上下文中注册和注销receiver,动态注册可以控制receiver的有效处理环境,当用户在一个具体的activity里注册receiver时,系统的ActivityMangerService会维持一个动态哈希表,以具体receiver对象为键,以该receiver对象所能接受处理的intentfilter列表为值,每当一个广播来临时,AMS就会对比广播中的intent-action,找到可以处理的receivers并调用他们的onReceiver方法,动态注册和注销的过程本质上就是上述哈希表的增加和删除,此类注册多用于和当前UI更新相关的操作,典型应用场景是界面进度条及依赖于后台服务的实时更新,动态注册的好处是可以建立只关注于当前上下文环境的接收器并建立处理流程,一旦activity被销毁或隐藏,可以实时注销receiver,较静态注册增加了灵活性,但局限于组件内,无法一直处于活动状态。
      对于由静态注册产生的全局receiver,如何去注销他呢?两种方式,一种方法是在永久禁止该功能,需要在AndroidManifest中的<receiver>标签里加上
      android:enable = false
 android:enabled
Whether or not the broadcast receiver can be instantiated by the system — "true" if it can be, and "false" if not. The default value is "true".

The <application> element has its own enabled attribute that applies to all application components, including broadcast receivers. The<application> and<receiver> attributes must both be "true" for the broadcast receiver to be enabled. If either is "false", it is disabled; it cannot be instantiated.


Open source file: receiver-element.html
       是否实例化receiver,默认实现时true,application和receiver同时设置成true时才能够实例化变量,你可以通过该方法来永久禁止一个receiver。更为普遍的情况是你想在运行时动态开放/禁止该广播接收器,这就需要借助包管理器来动态更改组件状态了。
private void toggleGlobalReceiver(Context context){ComponentName componentName = new ComponentName(context, NormalBCReceiver.class);//检查广播接收器组件处于何种状态。int status = getPackageManager().getComponentEnabledSetting(componentName);Log.d(TAG, "status = " + status);if(status == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {Log.d(TAG, "preview status is enable");//禁止该广播接收器context.getPackageManager().setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED , PackageManager.DONT_KILL_APP);} else if(status == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {Log.d(TAG, "preview status is disable");//使能该广播接收器context.getPackageManager().setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_ENABLED , PackageManager.DONT_KILL_APP);} else if (status == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT){Log.d(TAG, "preview status is default");//使能该广播接收器context.getPackageManager().setComponentEnabledSetting(componentName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED , PackageManager.DONT_KILL_APP);}}<span style="font-family:微软雅黑;">     </span>
       大部分的系统广播都是可以被两种注册receiver接受处理的,有的比较特殊,如android.intent.action.SCREEN_OFF android.intent.action.SCREEN_ON,这两个类型的广播只能被动态注册的接收器处理,google希望大家能够在一个长时间运行的后台服务中注册该广播接收器,这样也比较好理解,如果每个人都可以写一个全局监听屏幕状态的广播接收器,那再谈用户体验基本上就是扯淡了。

Q3  发送方如何指定该广播可以被谁接收。

       发送方在发送一个广播时,不希望这个广播被任意一个符合条件的接受者所处理,广播作为一个系统级的进程通信手段,不能被随意获取,为了达到这个目的,可以通过引入权限来加以限制,Android中权限安全的管理机制已比较完善,但官方不建议第三方开发者随意自定义过多的权限,但目前来看,设置自定义权限确实是广播发送者限制接收范围的有效途径,当然,首先需要发送者在自己的配置文件里声明一个权限:   
            
      发送者在发送广播时,要求接受者拥有该权限,需要把该权限名写到发送函数中          
            
      对于有意处理该广播的接受者来说,必须向系统表明我需要使用该权限以接受广播,这样就需要在接受者应用程序的注册文件中标明:
             
       声明了权限后,接受者才可以正常接收处理广播信息,通过此类方式,发送者确定了能够接收广播的接受者范围,只有申请使用了发送者指定权限的接受者,才能够正常的接收和处理广播,ICS以后可以指定某个包名作为广播接受者,通过在Intent.setPackage实现。

Q4  接受方如何指定只接收谁发送的广播。

       不仅仅是广播发送方能够选择接收方,接收方同样有这样的需求,并不是谁的广播我都愿意接收,就像是某些系统级的服务,比如剪切板,剪切板的很多服务如拷贝、删除、粘贴等是由receiver组织起来的,receiver接收符合要求的发送方的广播,才会启动对应服务,其它没有获取指定权限的发送方,receiver是完全不用理会的。
       同样的,接受方首先需要在自己配置文件里声明一个权限
             
       接收方需要向系统说明,只接受申请使用了上述权限的发送方发来的广播,该说明是定义在接收方注册文件中的<receiver>标签里           
             
       对于发送方来说,唯一要做的就是向系统表明,我已经申请并使用了上述权限,也就是在发送应用的注册文件里标明             
             
       经过上述设置后,接受方就只会处理使用指定自己指定权限的发送方的广播,如果接受者只想要接受自己进程内部发出的广播,可以将在<receiver>标签内的设置android:exported=["true" | "false"]
       Q4和Q3属于一类问题,都是由提出需求的一方定义一个权限并且要求另一方使用该权限,否则就不予配合,过程似乎清晰,但是一个权限在被定义后,另一方只需要申明就可以使用吗?如果是这样权限就没有存在的必要了,先来看看下Android对于用户自己定义权限做了哪些方面的设置,具体内容如下
            
       其中的icon、label、description属性是具体定义的,大家在安装apk时可以看到很多权限要求,就是使用了这里的资源设置,permissionGroup属性是表示此权限隶属于哪个权限组,权限组可由<permission-group>标签设置。这里最为重要的protectionLevel属性,设置了权限的分级操作,官网上给出了这样一张图
             
          可以看出Android中权限分为四个等级,按照严密程度从宽到严依次是
     1)Normal,最低级别,任何应用都可以使用该权限,没有限制,使用时不会向用户提示,这种权限保护的基本上是应用级别的feature,并不涉及系统安全一类的操作,安装时用户可以看到此类权限信息。
     2)dangerous,较高级别,此类权限通常会涉及一些针对用户数据及安全隐私一类的操作,系统不会自动授权,而是提醒用户是否使用该功能,比如大家在浏览私人图片时,如想要往SNS上共享个人信息时会提示“是否开启数据通信”等提示。
     3)signature  签名权限,如果申请权限的应用和定义权限的应用使用的是统一签名,通常说来,其实就是同一个开发者或者开发团队,系统会自动向其授予权限而不会通知用户。
     4)signatureOrSystem  比签名权限多了个系统镜像,被编译进同一个系统镜像文件的应用可以自由获取权限,其实就是厂商内置应用权限,通常不建议使用因为签名权限已经足够大部分情况下,这种权限仅仅用在一些特殊场合,比如很多厂商的应用内置到同一个系统镜像中且需要共享数据时。
        这样看来,不是任何一个权限都可以被申请并使用的,很多时候要看权限的等级和申请者的签名等信息,由于默认情况下是Normal,所以上述Q4和Q3还是可以正常运行的。Android权限内容较为繁杂,本文暂不赘述。   

Q5  本地广播用法

       很多时候使用广播的目的仅仅是为了通信,而不一定是在进程间,比如Activity和后台服务的通信,进程内不同模块的通信。发送者希望广播仅仅在应用内部被获取和处理,可以通过使用本地广播接收器来实现。本地广播接收器的作用域要小一些,外部接收器不会接受到本地广播,所以效率较高。本地广播接收器需要有android 支持库,使用前需要配置好。一个典型的本地广播用法如下所示:     
//本地广播管理器,获取单例对象LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getApplicationContext());//注意:该广播接收器可以接受全局Intent广播,但无法接受其它应用发送过来的广播lbm.registerReceiver(new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {// TODO Auto-generated method stub//广播处理函数}}, new IntentFilter(ACTION_NORMAL_BC));//发送本地广播,此广播只可以被应用程序内部接收器接受处理lbm.sendBroadcast(new Intent(ACTION_NORMAL_BC));<span style="font-family:微软雅黑;">     </span>
       本地广播可以限制发送的广播仅仅被应用内部的组件接受或者处理,注册的接收器也无法接受接受其它应用发送的广播,避免成为安全漏洞。
小结:
       本文结合最近使用无序广播接收器遇到的问题,整理归纳了关于基础代码和权限相关的内容,官网上对于该组件的介绍比较简略,进程间通信需考虑很多问题诸如权限、隐私安全等内容,本文仅讨论无序广播,暂未涉及有序和Sticky类型广播,以后在开发使用时会汇总整理。
     
原创粉丝点击