静态注册与动态注册

一.静态注册的广播接收者居然不能接收广播

这是我们上课的时候,出现的一个问题,用静态注册,在清单文件中注册的广播接收者不能接收广播。
先上代码:
我们首先在清单文件中静态注册广播接收者,并且为其添加意图过滤器

        <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,这样系统就会知道该进程将继续活跃地工作。

发布了3 篇原创文章 · 获赞 4 · 访问量 2629

猜你喜欢

转载自blog.csdn.net/q2020117976/article/details/105334743
今日推荐