Android——详解广播机制

1.1广播机制简介

为了便于进行系统级别的广播通知,Android引入了一套广播消息机制,Android的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序只会接收到自己所关心的广播内容,这些广播可能是来自系统的,也可能是来自其他应用的,Android提供了一套完整的API,允许应用程序自由地接受和发送广播。接受广播的方法引入一个新概念——广播接收器(Broadcast Receiver)。
广播主要分为两种类型,标准广播,有序广播。

  • 标准广播:是一种完全异步执行的广播,在广播发出后,所有的广播几乎在同一时刻接受这条广播消息,没有顺序,效率比较高,同时意味着无法截断。
  • 有序广播:一种同步执行的广播,在广播发出后,同一时刻只会有一个广播接收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,此时的广播接收器是有先后顺序的,优先级高的广播就可以先接收到消息,并且前面的广播接收器还可以截断正在传递的广播,后面的就无法接收消息了。

1.2 接受系统广播

1.2.1 动态注册监听网络变化

注册广播:1.代码注册,动态注册。2.在AndroidManifest.xml中注册,静态注册
该如何创建一个广播接收器,只需要新建一个,让它继承自BroadcastReceiver,并重写父类的onReceive()方法。这样当有广播到来时,onReceive()方法就会得到执行,具体的逻辑可以在这个方法中处理
先写一个通过动态注册的方式监听网络变化的程序,新建一个项目

public class MainActivity extends AppCompatActivity {
    private IntentFilter intentFilter;
    private NetworkChangeReceiver networkChangeReceiver;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intentFilter = new IntentFilter();
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        networkChangeReceiver = new NetworkChangeReceiver();
        registerReceiver(networkChangeReceiver,intentFilter);
    }
    @Override
    protected void onDestroy(){
        super.onDestroy();
        unregisterReceiver(networkChangeReceiver);
    }
    class NetworkChangeReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent){
            Toast.makeText(context,"network changes",Toast.LENGTH_SHORT).show();
        }
    }
}

在MainActivity中定义了一个内部类NetworkChangeReceiver,继承BroadcastReceiver,并重写了父类的onReceive()方法,当网络状态发生变化时,onReceive()方法就会得到执行,Toast提示了一段文本。OnCreate()方法,首先创建了IntentFilter的实例,并给他添加了android.net.conn.CONNECTIVITY_CHANGE的action,因为当网络状态发生变化时,系统发出的正是一条值为android.net.conn.CONNECTIVITY_CHANGE的广播。所以我们的广播接收器想要监听什么广播,就在这里添加相应的action。接下来创建一个NetworkChangeReceiver实例,然后调用registerReceiver()方法进行注册,将NetworkChangeReceiver的实例和IntentFilter的实例都传进去。这样NetworkChangeReceiver就会收到值为android.net.conn.CONNECTIVITY_CHANGE的广播,也就实现了监听网络的功能。

最后,动态注册的广播接收器一定要取消注册,这里在onDestroy()方法中通过调用**unregisterReceiver()**方法来实现。
对以上代码进行优化,提醒当前是有网络还是没网络

class NetworkChangeReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context ,Intent intent){
        ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
        if(networkInfo!=null&&networkInfo.isAvailable()){
            Toast.makeText(context,"network is available",Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(context,"network is unavailable",Toast.LENGTH_SHORT).show();
        }
    }
}

在onReceive()方法中,通过getSystemService()方法得到了ConnectivityManager的实例,这是一个系统服务类,专门用于管理网络连接,然后调用他的getActivityNetworkInfo()方法可以得到NetworkInfo实例,接着调用NetworkInfo的isAvailable()方法就可以判断当前是否有网络,最后通过Toast提示。
要在配置文件里声明权限,在AndroidManifest.xml中加入

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

在这里插入图片描述
在这里插入图片描述

1.2.2 静态注册实现开机启动

动态注册的广播可以自由地控制注册与注销,灵活,但有一个缺点,必须要在程序启动后才能接收广播,因为注册的逻辑是写在onCreate()方法里,想要让程序在未启动的情况下就能接受广播,这就需要静态注册
让程序接受一条开机广播,当收到这条广播就可以在onReceive()方法里执行相应的逻辑,从而实现开机启动功能。可以使用Android Studio提供的快捷方式来创建一个广播接收器,New,Other,Broadcast Receiver,将广播接收器命名为BootCompleteReceiver,Exported属性表示是否允许这个广播接收器接受本程序以外的广播,Enabled属性表示是否启用这个广播接收器。修改BootCompleteReceiver中的代码

public class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"Boot Complete",Toast.LENGTH_SHORT).show();
    }
}

静态的广播器要在AndroidManifest.xml中注册,修改其中的代码

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

<receiver
    android:name=".BootCompleteReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>
......

在< aoolication >标签内出现了一个新标签< receiver > ,所有静态的广播接收器都是可以在这里进行注册,用法和< activity >标签非常相似,通过android:name来指定具体注册哪一个广播接收器。由于Android系统启动后会发出一条值为android.intent.action.BOOT_COMPLETED的广播,所以在< intent-filter >标签里添加相应的action。另外监听系统开机广播需要声明权限,在< uses-permission >标签里加入了一条android.permission.RECEIVE_BOOT_COMPLETED权限。

Android 8.0或更高版本,无法使用清单为大多数隐式广播声明接收器,开机时无法接收到广播,解决方法:

Intent it = new Intent("com.example.broadcast.BootCompleteReceiver");
it.setComponent(new ComponentName("com.example.broadcast", "com.example.broadcast.BootCompleteReceiver"));
sendBroadcast(it);

第一个参数是包名,第二个参数是广播器名,然后开机,效果如图
在这里插入图片描述
在真正项目中的时候,可以在onReceive()中写自己的逻辑,但是不要在其中添加过多的逻辑或进行任何耗时的操作,因为在广播接收器里是不允许开启线程的。所以广播接收器更多的扮演一种打开程序其他组件的角色,比如创建一条状态通知栏,或者启动一个服务。

1.3 发送自定义广播

1.3.1发送标准广播

发送广播之前先定义一个广播接收器来准备接受此广播,新建MyBroadcastReceiver,

public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"received in MyBroadcastReceiver",Toast.LENGTH_SHORT).show();
    }
}

修改AndroidManifest.xml中的代码

...
<receiver
    android:name=".MyBroadcastReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.broadcast.MY_BROADCAST"/>
    </intent-filter>
</receiver>
...

让MyBroadcastReceiver接受一条值为com.example.broadcast.MY_BROADCAST的广播,因此待会儿发送广播的时候,就需要发出这样一条广播。
修改activity_main.xml中的代码

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:text="Send Broadcast"/>

</LinearLayout>

然后修改MainActivity中的onCreate()的代码

...
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent("com.example.broadcast.MY_BROADCAST");
        intent.setComponent(new ComponentName("com.example.broadcast", "com.example.broadcast.MyBroadcastReceiver"));
        sendBroadcast(intent);
    }
});
...

在按钮的点击事件里加入了发送自定义的广播逻辑,首先构建出了一个Intent对象,并把要发送的广播的值传入,然后调用了Context的sendBroadcast()方法将广播发送出去,这样所有监听com.example.broadcast.MY_BROADCAST这条广播的广播接收器就会收到这条消息,此时发出去的广播是一条标准广播。
点击按钮,效果如下
在这里插入图片描述

1.3.2发送有序广播

广播是一种可以跨进程的通信方式,因此在我们应用内发出的广播,其他应用程序也可以收到。再创建一个新项目BroadcastTest2,在这个项目下定义一个广播接收器,用于接收上一节中的自定义广播,新建AnotherBroadcastReceiver

public class AnotherBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"received in AnotherBroadcast",Toast.LENGTH_SHORT).show();
    }
}

修改AndroidManifest.xml中的代码

...
<receiver
    android:name=".AnotherBroadcastReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.broadcasttest1.MY_BROADCAST"/>
    </intent-filter>
</receiver>
...

AnotherBroadcastReceiver同样接收的是com.example.broadcasttest1.MY_BROADCAST这条广播,先运行BroadcastTest2安装到模拟器上,然后修改Broadcast的MainActivity中的代码

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent("com.example.broadcast.MY_BROADCAST");
        intent.setComponent(new ComponentName("com.example.broadcast", "com.example.broadcast.MyBroadcastReceiver"));
        sendBroadcast(intent);
 
        Intent intent2 = new Intent("com.example.broadcast.MY_BROADCAST");
        intent2.setComponent(new ComponentName("com.example.broadcasttest2","com.example.broadcasttest2.AnotherBroadcastReceiver"));
        sendBroadcast(intent2);
    }
});

运行,点击按钮就会分别弹出两次提示信息。这样证明了,我们应用程序发出的广播可以被其他应用程序接收到。

1.4 使用本地广播

本地广播机制发出的广播只能在程序的内部进行传递,并且广播接收器也只能接受来自本应用程序发出的广播
主要使用LocalBroadcastManager来对广播进行管理。

public class MainActivity extends AppCompatActivity {
    private IntentFilter intentFilter;
    private LocalReceiver localReceiver;
    private LocalBroadcastManager localBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        localBroadcastManager = LocalBroadcastManager.getInstance(this); //得到实例
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent("com.example.broadcasttest1.LOCAL_BROADCAST");
                localBroadcastManager.sendBroadcast(intent); //发送本地广播
            }
        });
        intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.broadcasttest1.LOCAL_BROADCAST"); //注册本地广播监听器
        localReceiver = new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver,intentFilter);
    }
    @Override
    protected void onDestroy(){
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(localReceiver);
    }

    class LocalReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context,Intent intent){
            Toast.makeText(context,"received in local broadcast",Toast.LENGTH_SHORT).show();
        }
    }

在这里插入图片描述

本地广播无法通过静态注册的方式来接收。
优势:1.可以明确知道正在发送的广播不会离开我们的程序,因此不必担心机密数据泄露。2.其他程序无法将广播发送黄金我们程序的内部,因此不需要担心会有安全泄露。3.发送本地广播会比发送系统全局广播更高效。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值