面试

android BroadcastReceiver之无序广播、有

2021-03-31  本文已影响0人  duoduo7628

发送无序广播

    Intent intent = new Intent("com.sqw.testBroadcastReceiver.say.hi");
    sendBroadcast(intent);

随机将给定的意图广播给所有感兴趣的BroadcastReceivers。 这个调用是异步的; 它立即返回,您将继续在接收器运行时执行。任何app注册都可以接收,可以使用intent.setPackage(String)指定接收应用。没有传播任何结果来自接收者,接收者不能中止广播。 如果你想要允许接收方传播结果或中止广播,您必须使用以下命令发送有序广播 sendOrderedBroadcast(Intent,String)

发送有序广播

    Intent intent = new Intent("com.sqw.testBroadcastReceiver.say.hi");
    sendOrderedBroadcast(intent,null);

一次向一个接收器发送广播。当接收器逐个顺序执行时,接收器可以向下传递结果,也可以完全中止广播,使其不再传递给其他接收器。接收器的运行顺序可以通过匹配的 intent-filter 的 android:priority 属性来控制;具有相同优先级的接收器将按随机顺序运行。

关于 android:priority 的取值范围,官网给出的是 -1000 ~ 1000 ,但是看到很多人设置成2147483647(Integer.MAX_VALUE)这个值,可能因为 android:priority 的属性值是 integer 类型,系统会拿这个值和其他值做比较,结果怎么都是它最大了。

        <receiver android:name=".receiver.MyReceiver1"
            android:protectionLevel="normal"
            >
            <intent-filter
                android:priority="100">

                <action android:name="com.sqw.testBroadcastReceiver.say.hi"/>
                <category android:name="android.intent.category.DEFAULT"/>

            </intent-filter>
        </receiver>

代码中设置priority

        IntentFilter intentFilter = new IntentFilter("com.sqw.testBroadcastReceiver.say.hi");
        intentFilter.setPriority(100);

中断有序广播

public class MyReceiver2  extends BroadcastReceiver {

    private static final String TAG = "MyReceiver2";

    @Override
    public void onReceive(Context context, Intent intent) {

        Toast.makeText(context,"MyReceiver2 收到广播",Toast.LENGTH_SHORT).show();
        Log.e(TAG, "onReceive: MyReceiver2 收到广播");
        //终止广播像低优先级传递
        abortBroadcast();
    }
}

发送本地广播

            Intent intent = new Intent("com.sqw.testBroadcastReceiver.say.hi.local");
            LocalBroadcastManager.getInstance(context).sendBroadcast(intent);

BroadcastReceiver的设计初衷是全局性,可接收来自本应用和其他应用发过来的intent广播。这也同时给app带来了一定的安全风险。为了解决这个问题,LocalBroadcastManager横空出世。LocalBroadcastManager 只会将广播限定在当前应用程序中。LocalBroadcastManager 发送的广播不会离开你的应用程序,同样也不会接收来自其它应用程序的广播,因此你可以放心的在 LocalBroadcastManager 中传播敏感信息。同时由于LocalBroadcastManager不需要用到跨进程机制,因此相对 BroadcastReceiver 而言要更为高效。LocalBroadcastManager只在动态广播时使用,静态广播不能使用LocalBroadcastManager。

另外本地广播注册和反注册方式与有序无序广播不一样,需要用到 LocalBroadcastManager.getInstance(context).registerReceiver()

    LocalReceiver1 localReceiver1 = new LocalReceiver1();
    IntentFilter intentFilter = new IntentFilter("com.sqw.testBroadcastReceiver.say.hi.local");
    //注册本地广播
    LocalBroadcastManager.getInstance(context).registerReceiver(localReceiver1,intentFilter);

    if(localReceiver1 != null){
        //反注册本地广播
        LocalBroadcastManager.getInstance(context).unregisterReceiver(localReceiver1);
    }

粘性广播

            Intent intent = new Intent("com.sqw.testBroadcastReceiver.say.hi.sticky");
            sendStickyBroadcast(intent);

粘性广播通过 Context.sendStickyBroadcast() 函数来发送,用此函数发送的广播会一直滞留,当有匹配此广播的广播接收器被注册后,该广播接收器就会收到此条广播。使用此函数发送广播时,需要获得 BROADCAST_STICKY 权限:<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
sendStickyBroadcast() 只保留最后一条广播,并且一直保留下去,这样即使已经有广播接收器处理了该广播,当再有匹配的广播接收器被注册时,此广播仍会被接收。如果你只想处理一遍该广播,可以通过 removeStickyBroadcast() 函数实现。

粘性广播在Android5.0(API 21)的时候就不推荐使用了,他们不提供安全性(任何人可以访问它们),没有保护(任何人都可以修改它们)以及许多其他问题。推荐使用非粘性广播,所以下文不再讨论粘性广播。

广播的安全性

因为只要注册了广播接收者,就可以收到广播,比如A、B接收者都注册了广播,当你只想向A接收者发送广播时,结果B接收者也能收到广播,也许数据就被泄露了,这是我们都不愿意看到的。所以BroadcastRecevier提供了限制接收者的方法,就是在发送时指定接收者权限receiverPermission

            Intent intent = new Intent("com.sqw.testBroadcastReceiver.say.hi");
            String receiverPermission = "com.sqw.testBroadcastReceiver.send.RECEIVE_PERMISSION";
            sendBroadcast(intent,receiverPermission);

当然有序广播也可以指定接收者权限receiverPermission, 本地广播是不可以的。

            Intent intent = new Intent("com.sqw.testBroadcastReceiver.say.hi");
            String receiverPermission = "com.sqw.testBroadcastReceiver.send.RECEIVE_PERMISSION";
            sendOrderedBroadcast(intent,receiverPermission);

要说明一下的是这里的receiverPermission,是一个自定义权限,需要在清单文件中定义后才能使用。如果没有声明自定义权限,发送的广播将没有符合要求接收者。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.noahedu.my">

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <!--自定义权限-->
    <permission android:name="com.sqw.testBroadcastReceiver.send.RECEIVE_PERMISSION"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:name="MyApplication"
        android:theme="@style/AppTheme">

      ...
    </application>

</manifest>

这里的 <uses-permission /><permission />可能大家会不怎么明白,这里说一下。

<uses-permission /> :这个是申请权限。
比如要访问网络就使用<uses-permission android:name="android.permission.INTERNET"/>之后应用才有访问网络的权限。
<uses-permission android:name="android.permission.SEND_SMS"/>声明要使用这个权限后就可以发送短信了,当然这是个危险权限,android6.0之后就需要动态申请了。

<permission /> 这个是定义权限。就是声明一个新的权限,使用方法就如上面发送有权限限定的广播,应该还有其他使用方法,这里我暂时不知道。

当然这里也可以直接写系统定义的权限,这样可以不需要自定义权限,当然自定义权限安全性更高。

            Intent intent = new Intent("com.sqw.testBroadcastReceiver.say.hi");
            sendBroadcast(intent,Manifest.permission.SEND_SMS);

这里不讨论系统权限,还是使用自定义权限,那既然有定义权限,就有使用权限的地方,是的,如果你想接收到有权限限定的广播,就必须申请使用发送广播时定义的权限,也就是使用 <uses-permission />,这里申请上面发送广播时定义的权限。

<uses-permission android:name="com.sqw.testBroadcastReceiver.send.RECEIVE_PERMISSION"/>

这里你会想到一个问题,即使是在应用内发送广播,应用内难道也要申请权限吗?是的,也要申请权限,不然也是收不到广播的。
这样之后,再按正常流程注册广播,就可以收到有权限的广播了。

限制广播发送者

可以限制接收者是否能收到广播,当然也有限制发送者的。
限制接收者是,发送的时候限定了权限。就是接收的地方,要声明权限,才能收到广播。
那么限制发送者,也就是在接收的地方定义权限了,在发送的地方就要申请权限。具体请看

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.noahedu.testmyservice">

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <!--定义权限-->
    <permission android:name="com.sqw.testBroadcastReceiver.receive.RECEIVE_PERMISSION"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
      
        ...

        <!--android:permission 在广播接收者中使用定义的权限-->
        <receiver android:name=".receiver.ServiceReceiver"
            android:permission="com.sqw.testBroadcastReceiver.receive.RECEIVE_PERMISSION"
            >
            <intent-filter>

                <action android:name="com.sqw.testBroadcastReceiver.say.hi"/>
                <category android:name="android.intent.category.DEFAULT"/>

            </intent-filter>
        </receiver>

    </application>

</manifest>

这里receiverandroid:permission属性可以使用定义的权限。这样发送者必须申明使用这个权限,发送的广播这里才能收到。

  <uses-permission android:name="com.sqw.testBroadcastReceiver.receive.RECEIVE_PERMISSION"/>

发送的应用,清单文件这样申请使用这个权限之后,发送广播,接收方就可以收到了。

简单总结一下就是:

  1. (发/收)自定义权限(<permission />)
  2. (发/收)设置自定义权限 sendOrderedBroadcast(Intent,String) 或 android:permission
  3. (收/发)申请使用自定义权限 <uses-permission />

到这里,其实大家也会想到一个问题,那就是可以双向绑定,这样安全性会更高!代码就不贴了

protectionLevel

但是这样还是不够安全,如果有人反编译了代码,就会发现自定义权限了,数据还是可能会泄漏,那有没有更高级的方法呢,如果是一个公司的两个app,使用相同的签名,可以将安全级别提升到签名级别,使用android:protectionLevel

        <receiver android:name=".receiver.ServiceReceiver"
            android:permission="com.sqw.testBroadcastReceiver.receive.RECEIVE_PERMISSION"
            android:protectionLevel="signature">

            <intent-filter>

                <action android:name="com.sqw.testBroadcastReceiver.say.hi"/>
                <category android:name="android.intent.category.DEFAULT"/>

            </intent-filter>
        </receiver>

这样只有具有相同签名的app,并且申请了对应权限,这里才会收到广播了。安全性会提高很多。
如果只是在本应用内使用,推荐使用本地广播LocalBroadcastManager,效率会更高。

广播的性能问题

如果很多app注册了开机广播<action android:name="android.intent.action.BOOT_COMPLETED"/>,那么系统会一个一个启动这些app进程,这样势必会消耗很多系统资源,对用户体验造成严重影响。这种情况建议动态注册。

安全注意事项和最佳做法

参考:
goole官方-广播概览
Android四大组件之——广播
Android 广播权限保护
Android 基础知识3:四大组件之 Broadcast(广播)

上一篇下一篇

猜你喜欢

热点阅读