一.静态注册的广播接收者居然不能接收广播
这是我们上课的时候,出现的一个问题,用静态注册,在清单文件中注册的广播接收者不能接收广播。
先上代码:
我们首先在清单文件中静态注册广播接收者,并且为其添加意图过滤器
<receiver android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="jiuzhe_Zuishuai"></action>
</intent-filter>
</receiver>
然后在布局文件中设置了一个发送广播按钮的点击事件
android:onClick="send"
public void send(View view) {
Log.v("jiuzhe","send");
Intent intent=new Intent();
intent.setAction("jiuzhe_Zuishuai");
sendBroadcast(intent);
然后我们还需要编写一个自定义广播接收者的类
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//当广播接收者接收到了广播之后会回调这个onReceive()这个方法
Log.v("jiuzhe","自定义的广播接收者,接收到了zzz广播事件");
Log.v("jiuzhe",intent.getAction());
//用Logcat在控制台打出那个过滤的字符串
}
//修改了广播接收者一定要去清单文件manifests中去注册广播接收者
}
然后我们运行程序,观察结果:
发现并没有像我们想的那样,静态注册的广播接收者并没有接收到广播,没有在控制台上输出我们要它输出的两段话,这是为什么
通过官方文档我进行解释一下:
Android官方文档对静态注册和动态注册的解释:
https://developer.android.google.cn/guide/components/broadcasts#java
广播限制
这官方文档解释好像比较生硬,其实就是清单文件注册是属于隐式注册的广播,在Android 8以上已经取消了这种隐式,要求必须通过显示通过setPackage()方法或者是setComponent()方法进行接收广播。这样相当于你隐式注册的广播接收者变成了显式的广播接收者了。
二.动态注册的广播接收者需要在清单文件中加代码进行注册吗
我认为是不需要在清单文件中加代码进行多余的注册的,因为清单文件中的代码就是起一个注册作用,但是,java代码中写的代码已经起到了注册作用了。接下来通过实验来演示一遍
重点还是那三行注册的代码
public class MainActivity extends AppCompatActivity {
private MyBroadcastReceiver myBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//动态注册的步骤:
//1.创建广播接收者和隐式意图过滤器对象
//2.注册广播接收者,用两个参数将实例广播接收者和隐式意图过滤器进行绑定
myBroadcastReceiver=new MyBroadcastReceiver();
IntentFilter intentFilter=new IntentFilter("Tanglei_Zuishuai");
registerReceiver(myBroadcastReceiver,intentFilter);
}
按钮点击事件的代码
//发送广播
public void send(View view) {
Log.v("jiuzhe","send");
Intent intent=new Intent();
intent.setAction("jiuzhe_Zuishuai");
sendBroadcast(intent);
同时动态注册的销毁广播接收者的代码是必不可少的,不然可能导致内存泄漏
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myBroadcastReceiver);
}
然后我们注释掉在清单文件中的代码`
<!-- 注册广播接收者-->
<!-- 通过隐式意图过滤器intent-filter让MyBoradcastReceiver只对这个广播感兴趣-->
<!-- enable,exported是为了注册广播接收者-->
<!-- <receiver android:name=".MyBroadcastReceiver"-->
<!-- android:enabled="true"-->
<!-- android:exported="true">-->
<!-- <intent-filter>-->
<!-- <action android:name="jiuzhe_Zuishuai"></action>-->
<!-- </intent-filter>-->
<!-- </receiver>-->
结果:
我们得到了我们想要的结果,说明清单文件中的代码根本没用,在别的博客上也可以找到。
https://blog.csdn.net/gaolh89/article/details/53153698
到底是为什么?
查了一下官方文档,才发现现在已经没有进行动态注册和静态注册的区分了,而是分成了清单声明的接收器和上下文注册的接收器,一看到这里你肯定就知道,动态注册时不要在清单文件中注册,官方都将这两个进行了分开
三.清单声明的接收器和上下文注册的接收器
清单声明的接收器
如果您在清单中声明广播接收器,系统会在广播发出后启动您的应用(如果应用尚未运行)。
注意:如果您的应用以 API 级别 26 或更高级别的平台版本为目标,则不能使用清单为隐式广播(没有明确针对您的应用的广播)声明接收器,但一些不受此限制的隐式广播除外。在大多数情况下,您可以使用调度作业来代替。
要在清单中声明广播接收器,请执行以下步骤:
1.在应用清单中指定 元素。
<receiver android:name=".MyBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
</intent-filter>
</receiver>
Intent 过滤器指定您的接收器所订阅的广播操作。
2.创建 BroadcastReceiver 子类并实现 onReceive(Context, Intent)。以下示例中的广播接收器会记录并显示广播的内容:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//当广播接收者接收到了广播之后会回调这个onReceive()这个方法
Log.v("jiuzhe","自定义的广播接收者,接收到了zzz广播事件");
Log.v("jiuzhe",intent.getAction()); //用Logcat在控制台打出那个过滤的字符串
}
//修改了广播接收者一定要去清单文件manifests中去注册广播接收者
}
系统软件包管理器会在应用安装时注册接收器。然后,该接收器会成为应用的一个独立入口点,这意味着如果应用当前未运行,系统可以启动应用并发送广播。
系统会创建新的 BroadcastReceiver 组件对象来处理它接收到的每个广播。此对象仅在调用 onReceive(Context, Intent) 期间有效。一旦从此方法返回代码,系统便会认为该组件不再活跃。
上下文注册的接收器
要使用上下文注册接收器,请执行以下步骤:
1.创建 BroadcastReceiver 的实例。
private MyBroadcastReceiver myBroadcastReceiver;
2.创建 IntentFilter 并调用 registerReceiver(BroadcastReceiver, IntentFilter) 来注册接收器:
myBroadcastReceiver=new MyBroadcastReceiver();
IntentFilter intentFilter=new IntentFilter("jiuzhe_Zuishuai");
registerReceiver(myBroadcastReceiver,intentFilter);
只要注册上下文有效,上下文注册的接收器就会接收广播。例如,如果您在 Activity 上下文中注册,只要 Activity 没有被销毁,您就会收到广播。如果您在应用上下文中注册,只要应用在运行,您就会收到广播。
3.要停止接收广播,请调用unregisterReceiver(android.content.BroadcastReceiver)。当您不再需要接收器或上下文不再有效时,请务必注销接收器。
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myBroadcastReceiver);
}
请注意注册和注销接收器的位置,比方说,如果您使用 Activity 上下文在 onCreate(Bundle) 中注册接收器,则应在 onDestroy() 中注销,以防接收器从 Activity 上下文中泄露出去。如果您在 onResume() 中注册接收器,则应在 onPause() 中注销,以防多次注册接收器(如果您不想在暂停时接收广播,这样可以减少不必要的系统开销)。请勿在 onSaveInstanceState(Bundle) 中注销,因为如果用户在历史记录堆栈中后退,则不会调用此方法。
四.setPackage()setComponent()
对于单个广播,通过setComponent()方法
intent.setComponent(new ComponentName("com.example.kh15hw1",
"com.example.kh15hw1.MyBroadcastReceiver"));
第一个参数是带包路径的项目名,第二个参数是带包路径的广播接收者
对于多个广播,通过setPackage()方法
intent.setPackage("com.example.kh15hw1");
五.两种注册的区别
他们的区别主要是体现在对进程的影响
BroadcastReceiver 的状态(无论它是否在运行)会影响其所在进程的状态,而其所在进程的状态又会影响它被系统终结的可能性。例如,当进程执行接收器(即当前在运行其 onReceive() 方法中的代码)时,它被认为是前台进程。除非遇到极大的内存压力,否则系统会保持该进程运行。
但是,一旦从 onReceive() 返回代码,BroadcastReceiver 就不再活跃。接收器的宿主进程变得与在其中运行的其他应用组件一样重要。如果该进程仅托管清单声明的接收器(这对于用户从未与之互动或最近没有与之互动的应用很常见),则从 onReceive() 返回时,系统会将其进程视为低优先级进程,并可能会将其终止,以便将资源提供给其他更重要的进程使用。
因此,您不应从广播接收器启动长时间运行的后台线程。onReceive() 完成后,系统可以随时终止进程来回收内存,在此过程中,也会终止进程中运行的派生线程。要避免这种情况,您应该调用 goAsync()(如果您希望在后台线程中多花一点时间来处理广播)或者使用 JobScheduler 从接收器调度 JobService,这样系统就会知道该进程将继续活跃地工作。