Android探索之BroadcastReceiver具体使用以及安全性探究

前言:

最近的计划是学习一下iOS的NSNotificationCenter,突然想起来的Android的广播机制,所以还是觉得先对BroadcastReceiver来个全面的总结然后再去学习NSNotificationCenter。

BroadcastReceiver简介:

BroadcastReceiver是Android四大组件之一,广播是一种广泛运用的在应用程序之间传输信息的机制,而BroadcastReceiver 是对发送出来的广播进行过滤接收并响应的一类组件;广播接收者( BroadcastReceiver )用于接收广播 Intent ,广播 Intent 的发送是通过调用 Context.sendBroadcast() 、 Context.sendOrderedBroadcast() 来实现的。通常一个广播 Intent 可以被订阅了此 Intent 的多个广播接收者所接收。

广播的使用场景:

1.同一app内部的同一组件内的消息通信(单个或多个线程之间);

2.同一app内部的不同组件之间的消息通信(单个进程);

3.同一app具有多个进程的不同组件之间的消息通信;

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

5.Android系统在特定情况下与App之间的消息通信。

广播的分类:(摘自http://www.cnblogs.com/lwbqqyumidi/p/4168017.html)

       1).Normal Broadcast:普通广播

此处将普通广播界定为:开发者自己定义的intent,以context.sendBroadcast_"AsUser"(intent, ...)形式。具体可以使用的方法有:
sendBroadcast(intent)/sendBroadcast(intent, receiverPermission)/sendBroadcastAsUser(intent, userHandler)/sendBroadcastAsUser(intent, userHandler,receiverPermission)。
普通广播会被注册了的相应的感兴趣(intent-filter匹配)接收,且顺序是无序的。如果发送广播时有相应的权限要求,BroadCastReceiver如果想要接收此广播,也需要有相应的权限。

       2).System Broadcast: 系统广播

Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开启启动,网络状态改变,拍照,屏幕关闭与开启,点亮不足等等。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。

      3)Ordered broadcast:有序广播

有序广播的有序广播中的“有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的定义过程与普通广播无异,只是其的主要发送方式变为:sendOrderedBroadcast(intent, receiverPermission, ...)。

对于有序广播,其主要特点总结如下:

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

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

     4)Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)。

既然已经deprecated,此处不再多做总结。

     5)Local Broadcast:App应用内广播(此处的App应用以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.更加高效。

广播的使用方式:

动态注册:

  //注册广播
    private void registerReceiver(){
        IntentFilter dynamicFilter = new IntentFilter();
        dynamicFilter.addAction(ActionCodes.DYNAMICACTION);//添加动态广博Action
        registerReceiver(dynamicReceiver, dynamicFilter);
    }

    //解除注册
    private void unRegisterReceiver()
    {
        unregisterReceiver(dynamicReceiver);
    }

    //动态广播的Receiver
    private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {

            if(intent.getAction().equals(ActionCodes.DYNAMICACTION)){    //动作检测
                String msg = intent.getStringExtra("msg");
                String finalMsg= String.format("%s%s","CActivity----->收到广播:",msg);
                Log.e("dynamicReceiver",finalMsg);
                Toast.makeText(context, finalMsg, Toast.LENGTH_SHORT).show();
            }
        }
    };

一般情况在Activity/Fragment 的onCreate/onStart/onResume 中注册, 在onDestory/onStop/onPause 中解除注册,根据不同的需求选择不能的生命周期函数。

静态注册:

       <receiver
            android:name=".StaticReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="100">
             <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" />
            </intent-filter>
        </receiver>

发送广播:

                //发送普通广播
                Intent intent = new Intent();
                intent.setAction(ActionCodes.DYNAMICACTION);//设置Action
                intent.putExtra("msg", "我是普通广播(动态注册)");//添加附加信息
                sendBroadcast(intent);

                //发送有序广播
                Intent intent = new Intent();
                intent.setAction(ActionCodes.DYNAMICACTION);//设置Action
                intent.setPackage(getPackageName());//设置包名
                intent.putExtra("msg", "我是有序广播(动态注册)");//添加附加信息
                sendOrderedBroadcast(intent,null);

以上基本上可以满足广播的基本使用了,接下来我们在写个测试程序:分别在A,B,C三个Activity中动态注册广播,分别发送普通广播和和有序广播。

发送普通广播接收顺序:

分别给A,B,C三个Activity中动态注册广播的优先级设置未100,500,1000接收顺序:

附上设置优先级方式:

        IntentFilter dynamicFilter = new IntentFilter();
        dynamicFilter.addAction(ActionCodes.DYNAMICACTION);//添加动态广播的Action
        dynamicFilter.setPriority(1000);//设置优先级
        registerReceiver(dynamicReceiver, dynamicFilter);

上文已知有序广播可以修改广播信息传递给下一级优先级低的接收者,我们让BActivity修改 让AActivity接收:

BActivity 广播实现

 //动态广播的Receiver
    private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent.getAction().equals(ActionCodes.DYNAMICACTION)){    //动作检测
                String msg = intent.getStringExtra("msg");
                String finalMsg= String.format("%s%s","BActivity----->收到广播:",msg);
                Log.e("dynamicReceiver",finalMsg);
                Toast.makeText(context, finalMsg, Toast.LENGTH_SHORT).show();

               if(isOrderedBroadcast()) {
                   //创建一个Bundle对象,并存入数据
                   Bundle bundle = new Bundle();
                   bundle.putString("msg", msg + "来自BActivity");
                   //将bundle放入结果中
                   setResultExtras(bundle);
                   //取消Broadcast的继续发送
                   //abortBroadcast();
               }
            }
        }
    };

AActivity 如何接收:

   //动态广播的Receiver
    private BroadcastReceiver dynamicReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent.getAction().equals(ActionCodes.DYNAMICACTION)) {    //动作检测

                String msg = intent.getStringExtra("msg");
                String finalMsg = String.format("%s%s", "AActivity----->收到广播:", msg);
                Log.e("dynamicReceiver", finalMsg);
                if (isOrderedBroadcast()) {
                    Bundle bundle = getResultExtras(true);//接收来自上一级优先级较高的广播修改的信息
                    String from = bundle.getString("msg");
                    if (TextUtils.isEmpty(from)) {
                        return;
                    }
                    Log.e("dynamicReceiver", String.format("%s%s", "AActivity----->收到广播:", from));
                    Toast.makeText(context, finalMsg, Toast.LENGTH_SHORT).show();
                }
            }
        }
    };

运行结果:

有序广播如何终止广播传播:

                 // 终止Broadcast的继续发送
                   abortBroadcast();

运行结果:

静态注册的广播上述测试运行结果一致,设置优先级方式不同而已:

        <receiver
            android:name=".AStaticReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="100"><!--设置优先级-->
                <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" />
            </intent-filter>
        </receiver>

接下来测试一下app应用之间发送广播,发送方式也是通过隐式Intent方式:

动态注册广播接收情况:

普通广播:

有序广播:

静态注册广播接收情况:

普通广播:

有序广播:

看了上述测试结果基本上和app内运行效果一模一样,所以按照上述那种注册方式和使用方式,一旦app被反编译之后有一定的安全隐患,如何安全的传输呢?

第一种方式:

静态注册广播可以设置:android:exported="false"

          <receiver
            android:name=".AStaticReceiver"
            android:enabled="true"
            android:exported="false" <!--设置只能接收app内广播 -->
            >
            <intent-filter android:priority="100"><!--设置优先级-->
                <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" />
            </intent-filter>
        </receiver>

第二种方式:通过设置发送的广播只能app内接收

                Intent intent = new Intent();
                intent.setAction(ActionCodes.DYNAMICACTION);//设置Action
                intent.setPackage(getPackageName());//设置包名使广播只能被app内接收者接收
                intent.putExtra("msg", "我是普通广播(动态注册)");//添加附加信息
                sendBroadcast(intent);

第三种方式通过自定义权限:通过上述两种方式只能达到屏蔽外来广播以及广播只在app内传播,无法实现app之间安全发送广播

自定义权限:

    <permission android:name="com.whoislcj.broadcastreceiver.MySelfBroadcastReceiver" />

    <uses-permission android:name="com.whoislcj.broadcastreceiver.MySelfBroadcastReceiver"/>

动态注册:

        IntentFilter dynamicFilter = new IntentFilter();
        dynamicFilter.addAction(ActionCodes.DYNAMICACTION);//添加动态广播的Action
        dynamicFilter.setPriority(500);
        //设置权限
        registerReceiver(dynamicReceiver, dynamicFilter,ActionCodes.MYPERMISSION,null);

静态注册:

         <receiver
            android:name=".BStaticReceiver"
            android:enabled="true"
            android:exported="true"
            <!--设置权限-->
            android:permission="com.whoislcj.broadcastreceiver.MySelfBroadcastReceiver">
            <intent-filter android:priority="500">
                <action android:name="com.whoislcj.broadcastreceiver.staticreceiver" />
            </intent-filter>
        </receiver>

发送广播:

                //普通广播
                sendBroadcast(intent,ActionCodes.MYPERMISSION);

               //有序广播
                sendOrderedBroadcast(intent,ActionCodes.MYPERMISSION);

第四种方式:通过LocalBroadcastManager方式

注册:

LocalBroadcastManager.getInstance(getInstance()).registerReceiver(receiver, filter);

解除注册:

LocalBroadcastManager.getInstance(getInstance()).unregisterReceiver(receiver);

发送广播:

LocalBroadcastManager.getInstance(getInstance()).sendBroadcastSync(intent);

总结:

通过本文可以看出BroadcastReceiver使用方式虽然看似简单,想要实现比较完善的广播还是要费一番功夫的。

时间: 2024-11-04 20:57:29

Android探索之BroadcastReceiver具体使用以及安全性探究的相关文章

Android 广播接收者 BroadcastReceiver

Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器).广播作为Android组件间的通信方式,可以使用的场景如下:1.同一app内部的同一组件内的消息通信(单个或多个线程之间): 2.同一app内部的不同组件之间的消息通信(单个进程): 3.同一app具有多个进程的不同组件之间的消息通信: 4.不同app之间的组件之间消息通信: 5.Android系统在特定情况下与App之间的消息通信. 从实现原理看上,Andro

Android面试,BroadCastReceiver的两种注册方式的异同

在Android手机应用程序中开发中,需要用到BroadcastReceiver来监听广播的消息.在自定义好BroadcastReceiver ,需要对其进行注册,注册有两种方法: 一种是在代码当中注册,注册的方法是registerReceiver(receiver,filter)(用Activity的实例来调用),取消注册的方法:unregisterReceiver(receiver),如果一个BroadcastReceiver用于更新UI(User Interface),那么通常会使用这种方

Android开发之BroadcastReceiver详解

BroadcastReceiver,顾名思义就是"广播接收者"的意思,它是Android四大基本组件之一,这种组件本质上是一种全局的监听器,用于监听系统全局的广播消息.它可以接收来自系统和应用的的广播. 由于BroadcastReceiver是一种全局的监听器,因此它可以非常方便地实现系统不同组件之间的通信.比如Activity与通过startService()方法启动的Service之间通信,就可以借助于BroadcastReceiver来实现. BroadcastReceiver简

Android开发之BroadcastReceiver的使用

1.静态注册. 在manifest中注册. 1 <receiver android:name="com.exce.learnbroadcastreceiver.MyReceiver"></receiver> 然后在activity中使用sendBroadcast(Intent)方法.其中参数是Intent. 1 Intent intent=new Intent(this,MyReceiver.class); 2 intent.putExtra("dat

Android开发之BroadCastReceiver

创建广播接收者:只需新建一个类继承自BroadcastReceiver,然后重写onReceive方法 注册广播的方式有两种: 1.静态注册:即在清单文件中注册,例如下面监听打电话的广播: <receiver android:name=".MyBroadcastReceiver"> <intent-filter> <action android:name="android.intent.action.NEW_OUTGOING_CALL"

android笔记:BroadcastReceiver

android允许应用程序自由地发送和接收广播. 广播是通过Intent进行数据传递的.接收广播则通过Broadcast Receiver(广播接收器)实现. 广播分为:标准广播和有序广播. 标准广播:一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言. 有序广播:广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了.

Android中 广播BroadcastReceiver

一.IP拨号器 activity_main.xml文件: 只有一个输入ip的文本框和一个保存按钮, <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_h

Android 利用Service BroadcastReceiver实现小例子

Activity: 1 package com.example.test; 2 3 import android.app.Activity; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.content.IntentFilter; 7 import android.os.Bundle; 8 import android.view.Menu; 9 import android.

Android四大套件BroadCastReceiver

概述:BroadcastReceiver用于接收并处理广播通知(broadcastannouncements). 多数的广播是系统发起的,如地域变换.电量不足.来电来信等.程序也可以播放一个广播.程序可以有任意数量的 broadcast receivers来 响应它觉得重要的通知.broadcast receiver可以通过多种方式通知用户:启动activity.使用 NotificationManager.开启背景灯.振动设备.播放声音等,最典型的是在状态栏显示一个图标,这样用户就可以点它打开