Android 四大组件 Broadcast 广播

时间:2022-12-21 12:33:11

前言:

国庆佳节之际,舍友都抛弃我回家了,只好自己在宿舍撸撸代码了。
前些日子撸了好几天才出来的《Android 四大组件 Service 服务》这篇博文被推荐到了首页,这对于新手的我来说,真是莫大的鼓励啊…

广播的套路都差不多,比较简单,今天就随着第一行代码撸了一下 Broadcast,可能有写的不对的地方,欢迎私信指正!


1.广播机制简介

在Android系统中,Broadcast是一种运用在应用程序之间传输信息的机制。

了解计算机网络的会知道,在一个 IP 网络范围中最大的 IP 地址是被保留作为广播地址来使用的。比如某个网络的 IP 范围是 192.168.0.XXX,子网掩码是 255.255.255.0,那么这个网络的广播地址就是 192.168.0.255。 广播数据包会被发送到同一网络上的所有端口,这样在该网络中的每台主机都将会收到这条广播。

我们拿收音机的广播电台来打比方:
电台发送的内容是语音,而Android中发送广播的内容是携带要传送的数据的Intent。
电台使用大功率的发射器发送内容,而Android中使用sendBroadcast()这个方法来发送。
电台用户通过调整电台频率接收电台的内容,而在Android中通过注册BroadCastReceiver来接收,只有发送广播的action和接收广播的action相同,接收器才能接收这个广播。

Android中每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收到自己所关心的广播内容,这些广播可能是来自于系统的,也可能是来自于自己或其他应用程序的。


2.广播类型

Android中的广播主要分为两种类型:普通广播和有序广播。

1) 普通广播(Normal broadcasts)是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序,这种广播的效率会比较高,但也意味着它是无法被截断的,偷图如下:

Android 四大组件 Broadcast 广播

2) 有序广播(Orderedbroadcasts)则是一种同步执行的广播,在广播发出之后,同一时刻只有一个广播接收器能收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,就像传递火炬一样,所以此时的广播接收器是有先后顺序的,优先级高的广播接收器可以先收到广播消息,并且位于前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播了,偷图如下:

Android 四大组件 Broadcast 广播


3.接收系统广播

Android内置了多种系统级别的广播,我们可以在应用程序中通过监听系统广播来得到各种系统状态信息。
比如手机开机完成后会发出一条广播,电池的电量发生变化会发出一条广播,时间或时区发生改变也会发出一条广播,网络状态改变发出一条广播等。

注册广播的方式一般有两种:在代码中注册和在 AndroidManifest.xml中注册,其中前者被称为动态注册,后者被称为静态注册

如果想要接收到这些广播,就需要使用广播接收器,广播接收器可以随心地对广播进行注册,当有相应的广播发出时,广播接收器就能够收到该广播,并在内部进行逻辑处理。那么该如何创建一个广播接收器呢?
我们需要新建一个类,让它继承自BroadcastReceiver, 并重写父类的 onReceive()方法。当有广播时,onReceive()方法就会得到执行,可以在这个方法中处理具体的逻辑。

那我们就先用动态注册的方式写一个能够监听网络变化的程序,简单学习一下广播接收器的基本用法,如下:


3.1)动态注册监听网络变化

第一步 创建广播接收器

新建NetworkChangeReceiver.java类的代码:

public class NetworkChangeReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni != null && ni.isAvailable()) {
Toast.makeText(context, "当前网络可用!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "请检查网络状态!", Toast.LENGTH_SHORT).show();
}
}
}

这个广播接收器类是继承自 BroadcastReceiver的,并重写了父类的 onReceive()方法。这样每当网络状态发生变化,就会执行onReceive()方法。在onReceive()方法中,通过 getSystemService()方法得到了 ConnectivityManager的实例,这是一个用于管理网络连接的系统服务类。然后调用它的 getActiveNetworkInfo() 方法可以得到 NetworkInfo的实例,接着调用 NetworkInfo的 isAvailable()方法,就可以判断出当前是否有网络,通过 Toast的方式对用户进行提示。

第二步 注册广播

修改MainActivity.java类的代码:

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);

createNetworkChangeReceiver();
}

private void createNetworkChangeReceiver() {
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver, intentFilter);
}

@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(networkChangeReceiver);
}
}

我们来看onCreate()方法:

首先创建了一个 IntentFilter的实例,并给它添加了一个值为 “android.net.conn.CONNECTIVITY_CHANGE” 的 action。当网络状态发生变化,系统发出的正是action为这条值的广播,也就是说我们的广播接收器想要监听什么广播,就在这里添加相应的 action就可以了。

然后创建了一个 NetworkChangeReceiver类的实例,然后将 NetworkChangeReceiver 的实例和 IntentFilter 的实例都传入 registerReceiver() 方法进行注册广播接收器。这样 NetworkChangeReceiver就会收到所有值为 ”android.net.conn.CONNECTIVITY_CHANGE” 的广 播,实现了监听网络变化的功能。

最后,动态注册的广播接收器一定都要取消注册,这里是在 onDestroy() 方法中通过调用 unregisterReceiver()方法来实现的。

第三步 声明权限

查询系统的网络状态就是需要在配置文件中声明权限的。打开AndroidManifest.xml文件,在里面加入如下权限就可以查询系统网络状态了:

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

运行:拉下通知栏,开关WiFi或者数据流量 会得到Tosat提示信息。


3.2)静态注册实现开机启动

动态注册的广播接收器可以随心地注册与销毁,灵活性比较高,但是必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在 onCreate()方法中的。
那么有没有什么办法可以让程序在未启动的情况下就能接收到广播呢?

这就需要使用静态注册的方式了。我准备让程序接收一条开机广播,当收到这条广播时就可以在 onReceive()方法里执行相应的逻辑,从而实现开机启动的功能。

第一步 创建广播接收器

新建一个 BootCompleteReceiver 继承自 BroadcastReceiver。

public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "郭峰的开机广播", Toast.LENGTH_LONG).show();
}
}

第二步 注册广播

在AndroidManifest.xml中注册广播接收器。

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

在 android:name 属性指定具体注册哪一个广播接收器
在 intent-filter 标签里加入想要接收的广播
(Android系统启动完成后会发出一条值为android.intent.action.BOOT_COMPLETED的广 播)

第三步 声明权限

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

特别注意:

我们在广播接收器的 onReceive()方法中都只是简单地使用 Toast提示文本信息,当在项目中使用它的时候,可以在里面编写自己的逻辑。
但是不要在 onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收 器中是不允许开启线程的,当 onReceive()方法运行了较长时间而没有结束时,程序就会报错。
因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知Nofitication,或者启动一个服务Service等。


4.发送自定义广播

上述两个简单例子仅仅通过 创建广播接收器来获取系统广播带来的信息,接下来看看发送广播怎么玩。
当然了,在发送广播之前,需要先定义一个广播接收器来接收我们要发送的广播,
这样才能看出效果嘞。

4.1)发送标准广播

第一步 创建广播接收器

新建一个MyBroadcastReceiver继承自BroadcastReceiver,上代码:

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

当MyBroadcastReceiver收到自定义的广播时,就会弹出这是MyBroadcastReceiver 的提示

第二步 静态注册广播

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

这里让 MyBroadcastReceiver 接收一条值为com.example.broadcasttest. MY_BROADCAST的广播,因此一会我们在发送广播的时候,就需要发出这样的一条广播。

第三步 发送广播

修改Activity的布局代码,加一个按钮,然后在java文件中的按钮点击监听事件中加入发送自定义广播的逻辑代码:

Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
sendBroadcast(intent);

首先New出一个Intent对象,把要发送的广播值传入,然后调用了 sendBroadcast()方法将广 播发送出去,这样所有监听 com.example.broadcasttest.MY_BROADCAST这条广播的接收器就会收到消息。此时发出去的广播就是一条简单的标准广播。

当然,广播是使用 Intent进行传递的,因此还可以在Intent中携带一些数据传递给广播接收器,然后在接收器中抠出来。


4.2)发送有序广播

广播是一种可以跨进程的通信方式,这一点从前面接收系统广播的时候就可以看出来。因此在我们应用程序内发出的广播,其他的应用程序也是可以收到的。

比如,再模仿MyBroadcastReceiver写一个MyBroadcastReceiver2接收器,同样也在配置文件中注册一下,然后运行、观察Toast的信息,会发现两个接收器都能收到广播,只要监听这条广播的接收器都能收到。内容很简单,就不上代码了…

上面写的都是普通广播,下面说一下有序广播吧,发送有序广播只需要改动一行代码,即将 sendBroadcast()方法改成 sendOrderedBroadcast()方法:

sendOrderedBroadcast(intent,null);

第一个参数仍然是Intent
第二个参数是与权限相关的字符串:String receiverPermission

改完后,运行,两个接收器还是都能收到广播,但是这个时候的广播接收器是有先后顺序的,而且前面的广播接收器可以将广播截断,阻止其继续传播。

那么如何设定广播接收器的先后顺序呢?
当然是在注册的时候进行设定的了,修改 AndroidManifest.xml中的代码:

<receiver android:name=".broadcast.MyBroadcastReceiver2">
<intent-filter android:priority="100">
<action android:name="com.example.broadcasttest.MY_BROADCAST" />
</intent-filter>
</receiver>

上面代码我在第二个接收器加了个android:priority=”100”,优先级设置成100,运行,发现果然2比1先执行。

那么,我已经可以决定接收广播的优先权了,我不想传递给后面的接收器需要怎么做呢?
在 onReceive()方法中调用abortBroadcast()方法就可以将这条广播截断,后面的接收器将无法再接收到这条广播。我在2里面调用这个方法,运行后,2提示信息,1不会提示,说明在2的时候终止往后传递了。


5.使用本地广播

上面所玩的都是系统全局广播,发出的广播可以被其他任何程序接收到,当然我们也可以接收来自其他任何应用程序的广播。这样安全性貌似不太高啊,比如发送的一些带有重要数据的广播有可能被其他的程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。
为了能够简单地解决广播的安全性问题,Android引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了…

本地广播主要使用 LocalBroadcastManager来对广播进行管理,并提供了发送广播和注册广播接收器的方法。

广播接收器代码如下:

public class LocalReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "接受的本地广播!", Toast.LENGTH_SHORT).show();
}
}

修改活动代码如下:

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);
createLocalBroadcast();
}

private void createLocalBroadcast() {

// 获取实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);

intentFilter = new IntentFilter();
intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver, intentFilter);

// 发送本地广播
Intent intent = new Intent("com.example.broadcasttest.LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);

}

@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(localReceiver);
}
}

有没有感觉套路很眼熟??没错,这基本上和前面的动态注册广播接收器以及发送广播的代码是一样。

只不过现在首先是通过LocalBroadcastManager的getInstance()方法得到了它的实例,然后在注册广播接收器的时候调用的是 LocalBroadcastManager 的registerReceiver()方法,在发送广播的时候调用的是LocalBroadcastManager的sendBroadcast() 方法。

注意:
本地广播是无法通过静态注册的方式来接收的。因为静态注册主要就是为了让程序在未启动的情况下也能收到广播,而发送本地广播时,我们的程序肯定是已经启动了,因此也完全不需要使用静态注册的功能。


6.广播常用的Action常量

用到什么,就问度娘,网上一大片,就不贴了…