Android O系统之后,Android系统对于广播的使用进行了限制,目的是避免广播滥用造成系统资源短缺,内存抖动和耗电等问题。

总之,大量的广播使用不仅耗电,还会影响系统性能。

关于大量发送广播为啥会耗电,大量app注册了静态广播(Manifest注册),当系统发送一个广播时,会唤醒多个app,需要启动app去接收广播,但是没有足够的内存让所有的app进行在缓存中处理,即内存有限,启动一个app后又kill掉另一个app进程。系统会在各个app进程之间跳动。

Android P的限制:

1.警告信息:
03-26 23:29:47.424  2663  2807 E ActivityManager: Sending non-protected broadcast android.media.extra.DSP_PCHIME from system 2663:system/1000 pkg android
03-26 23:29:47.424  2663  2807 E ActivityManager: java.lang.Throwable
03-26 23:29:47.424  2663  2807 E ActivityManager: 	at com.android.server.am.ActivityManagerService.checkBroadcastFromSystem(ActivityManagerService.java:21398)

系统对发送普通广播进行了检查,如果在系统中发送了未被保护的广播,就会出现上述警告。

规范:

1.如果是在系统里发送广播,即系统应用给系统发送广播,或者系统应用给系统应用发送广播,或者是系统给系统应用发,必须给广播加上保护,frameworks\base\core\res\AndroidManifest.xml中,添加:

<protected-broadcast android:name="android.media.extra.DSP_PCHIME" />

2.如果是普通app间的广播,对于广播发送者,必须在intent中指明包名;对于广播注册者,静态广播,exported为true的时候,必须加上permission限制,对于发送者的限制,如果发送者没有添加该permission权限(),则发送的广播,接收端是不会接收的。

//自定义权限
<permission android:name="io.github.mindjet.SEND_PERMISSION" />

<receiver android:name="MyBroadcastReceiver"
        android:permission="io.github.mindjet.SEND_PERMISSION">
    <intent-filter>
            <action android:name="io.github.mindjet.JUST_BROADCAST" />
    </intent-filter>
</receiver>

如果是动态广播,在filter中指定permission限制。当然要先在Manifest.xml中自定义。

//自定义权限
<permission android:name="io.github.mindjet.SEND_PERMISSION" />
2.警告信息:

ContextImpl: Calling a method in the system process without a qualified user: android.app.ContextImpl.sendBroadcast

系统或者使用了android:sharedUserId="android.uid.system"的系统应用在调用Context接口的时候,必须指定发送广播的user。

Intent intent = new Intent();
    intent.setAction(ACTION_TTS);
    intent.putExtra(EXTRA_TTS, isGain ? 1 : 0); // 1: gain audio focus, 0: release audio focus
    intent.addFlags(FLAG_RECEIVER_INCLUDE_BACKGROUND);
    context.sendBroadcast(intent);

修改为:

context.sendBroadcastAsUser(intent, UserHandle.SYSTEM);

如何判断应用是否为系统应用:
命令:ps -A
可以查看USER,root,system,media,bluetooth,u0_a13,u0_a46,u0_a28等等。

user为system的,即为系统应用。

当一个app为非系统应用时,缺使用了被保护的广播,就会报错:

java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.com.hsae.speech.service.action.ACTION_VR_TTS_SET_CHAN from pid=4315, uid=10068
        at android.os.Parcel.createException(Parcel.java:1950)
        at android.os.Parcel.readException(Parcel.java:1918)
        at android.os.Parcel.readException(Parcel.java:1868)
        at android.app.IActivityManager$Stub$Proxy.broadcastIntent(IActivityManager.java:3894)

总结,被保护的广播只能被系统使用。系统中的广播不需要指定包名,但是一定要加保护。