如果应用注册为接收广播,则在每次发送广播时,应用的接收器都会消耗资源。 如果多个应用注册为接收基于系统事件的广播,则会引发问题:触发广播的系统事件会导致所有应用快速地连续消耗资源,从而降低用户体验。 为了缓解这一问题,Android 7.0(API 级别 24)对广播施加了一些限制,如后台优化中所述。 Android 8.0(API 级别 26)让这些限制更为严格。
适配 Android 8.0 或更高版本的应用无法继续在其清单中为隐式广播注册广播接收器。 隐式广播是一种不专门针对该应用的广播。 例如,ACTION_PACKAGE_REPLACED 就是一种隐式广播,因为该广播将被发送给所有已注册侦听器,让后者知道设备上的某些软件包已被替换。 不过,ACTION_MY_PACKAGE_REPLACED 不是隐式广播,因为不管已为该广播注册侦听器的其他应用有多少,它都会只被发送给软件包已被替换的应用。
应用可以继续在它们的清单中注册显式广播。
应用可以在运行时使用 Context.registerReceiver() 为任意广播(不管是隐式还是显式)注册接收器。
需要签名权限的广播不受此限制所限,因为这些广播只会发送到使用相同证书签名的应用,而不是发送到设备上的所有应用。
在许多情况下,之前注册隐式广播的应用使用 JobScheduler 作业可以获得类似的功能。 例如,一款社交照片应用可能需要不时地执行数据清理,并且倾向于在设备连接到充电器时执行此操作。 之前,应用已经在清单中为 ACTION_POWER_CONNECTED 注册了一个接收器;当应用接收到该广播时,它会检查清理是否必要。 为了迁移到 Android 8.0 或更高版本,应用将该接收器从其清单中移除。 应用将清理作业安排在设备处于空闲状态和充电时运行。**请注意:**很多隐式广播当前已不受此限制所限。 应用可以继续在其清单中为这些广播注册接收器,不管应用适配哪个 API 级别。 有关已豁免广播的列表,请参阅隐式广播例外。
今天带大家看下如何发送与接收一个自定义权限的广播
首先这边创建了两个工程,MyBroadcastReceiverSendDemo(发送广播),MyBroadcastReceiver(接收广播),来模拟广播的接收与发送。
第一步,在发送自定义权限的广播时,我们需要在MyBroadcastReceiverSendDemo(发送广播)中的AndroidManifest.xml文件中声明权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<permission
android:name="com.xcy.broadcastreceiversenddemo.MY_PERMISSION"
android:protectionLevel="signature"/>
<uses-permission android:name="com.xcy.broadcastreceiversenddemo.MY_PERMISSION" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.BroadcastReciverSendDemo"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
自定义权限知识扩展
<permission android:description="string resource"
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permissionGroup="string"
android:protectionLevel=["normal" | "dangerous" |"signature" | ...] />
android:protectionLevel
说明权限中隐含的潜在风险,并指示系统在确定是否将权限授予请求授权的应用时应遵循的流程。每个保护级别都包含基本权限类型以及零个或多个标记。例如,dangerous保护级别没有标记。相反,保护级别 signature|privileged是signature基本权限类型和privileged标记的组合。
值 | 含义 |
normal | 默认值。具有较低风险的权限,此类权限允许请求授权的应用访问隔离的应用级功能,对其他应用、系统或用户的风险非常小。系统会自动向在安装时请求授权的应用授予此类权限,无需征得用户的明确许可(但用户始终可以选择在安装之前查看这些权限)。 |
dangerous | 具有较高风险的权限,此类权限允许请求授权的应用访问用户私人数据或获取可对用户造成不利影响的设备控制权。由于此类权限会带来潜在风险,因此系统可能不会自动向请求授权的应用授予此类权限。例如,应用请求的任何危险权限都可能会向用户显示并且获得确认才会继续执行操作,或者系统会采取一些其他方法来避免用户自动允许使用此类功能。 |
signature | 只有在请求授权的应用使用与声明权限的应用相同的证书进行签名时系统才会授予的权限。如果证书匹配,则系统会在不通知用户或征得用户明确许可的情况下自动授予权限。 |
signatureOrSystem | signature | privileged 的旧同义词。在API级别23中已弃用。系统仅向位于Android系统映像的专用文件夹中的应用或使用与声明权限的应用相同的证书进行签名的应用授予的权限。不要使用此选项,因为 signature 保护级别应足以满足大多数需求,无论应用安装在何处,该保护级别都能正常发挥作用。signatureOrSystem权限适用于以下特殊情况:多个供应商将应用内置到一个系统映像中,并且需要明确共享特定功能,因为这些功能是一起构建的。 |
第二步,在MainActivity.java中发送广播
package com.xcy.broadcastreceiversenddemo;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private final String TAG = "MainActivity";
private final String permission = "com.xcy.broadcastreceiversenddemo.MY_PERMISSION";
private final String action = "com.xcy.broadcastreceiversenddemo.MY_ACTION";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendBroadcastReceiver();
}
});
}
public void sendBroadcastReceiver(){
Log.d(TAG,"sendBroadcastReceiver");
Intent intent = new Intent();
intent.setAction(action);
sendBroadcast(intent,permission);
}
}
附上activity.main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送广播"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
第三步,在MyBroadcastReceiver(接收广播)工程里AndroidManifest.xml文件中注册权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="com.xcy.broadcastreceiversenddemo.MY_PERMISSION" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.BroadcastReceiverDemo"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 或者选择静态注册的方式,在xml中配置广播 -->
<!-- <receiver-->
<!-- android:name=".MyBroadcastReceiver"-->
<!-- android:exported="true"-->
<!-- android:permission="com.xcy.broadcastreceiversenddemo.MY_PERMISSION">-->
<!-- <intent-filter>-->
<!-- <action android:name="com.xcy.broadcastreceiversenddemo.MY_ACTION" />-->
<!-- </intent-filter>-->
<!-- </receiver>-->
</application>
</manifest>
第四步,在MyBroadcastReceiver(接收广播)工程里MainActivity.java中注册广播监听
package com.xcy.broadcastreceiverdemo;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
private final String TAG = "MainActivity";
private final String permission = "com.xcy.broadcastreceiversenddemo.MY_PERMISSION";
public static final String action = "com.xcy.broadcastreceiversenddemo.MY_ACTION";
private MyBroadcastReceiver broadcastReceiver = new MyBroadcastReceiver();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate -> registerReceiver");
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(action);
registerReceiver(broadcastReceiver, intentFilter, permission, null, Context.RECEIVER_EXPORTED);
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy -> unregisterReceiver");
unregisterReceiver(broadcastReceiver);
}
}
MyBroadcastReceiver.java
package com.xcy.broadcastreceiverdemo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
public class MyBroadcastReceiver extends BroadcastReceiver {
private final String TAG = MyBroadcastReceiver.class.getSimpleName();
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive");
if (intent != null) {
String mAction = intent.getAction();
switch (mAction) {
case MainActivity.action:
Log.d(TAG, "onReceiver -> action=" + mAction);
Toast.makeText(context, "onReceive action="+mAction, Toast.LENGTH_SHORT).show();
break;
}
}
}
}
ok,让我们先Run下MyBroadcastReceiver(接收广播)工程,然后Run下MyBroadcastReceiverSendDemo(发送广播)工程,点击Button。 nice ~ 完结撒花❀
Ps:需要完整code的小伙伴,可以留言。