1.引言
在《第四章 Android 四大应用组件》中,简单介绍了下四大组件的成员,属性,生命周期等。这里主要是介绍下Android中的广播机制。在四大组件中,Activity的使用频率是最高的。其他三个组件的使用频率相对叫低,但是既然叫四大组件,那就说明了他们的不可或缺性。
记得小时候上学的时候的大喇叭吗?有时候听力考试也会用到,学校的广播室通过发送广播到各个教室的小喇叭上,每次一开,那简直是要命啊 。类似于喇叭的工作机制,在现在的计算机领域会有很广泛的引用。为了便于系统级别的消息通知,Android也引入了广播消息机制。当然相应的比较那个大喇叭例子,Android的广播机制更加灵活。
2.简介
为什么说Android的广播机制灵活呢?我们知道在我们的手机上有很多个应用,有时候会接收到不同的应用的消息,但是,既然是大喇叭,发过来的消息,不是都可以接受么?这里简单讲一下它的灵活之处,Android中的每个应用程序都可以对自己感兴趣的广播注册,这样该程序就只能接收到自己关心的广播内容。这些广播的内容可以是来自于系统,也可以是来自其他应用程序。
我们所使用的手机,既可以说是一个广播接收器,也可以说是一个广播发送器。Android 的广播分为两个方面:广播发送者和广播接收者。BroadCast Receiver指的是广播接受者(广播接收器)。在一些系统的广播的使用场景:电量低弹窗,开机,锁屏等。常见的广播使用场景有下面几种:
1.同一app内部的同一组件内的消息通信(单个或多个线程之间)
2.同一app内部的不同组件之间的消息通信(单个进程);
3.同一app具有多个进程的不同组件之间的消息通信;
4.不同app之间的组件之间消息通信;
5.Android系统在特定情况下与App之间的消息通信。
之所以叫做广播,就是因为它只管"说"至于"听不听",那就不管了。另外广播的另一个特点是,它可以被不止一个程序接收,也可以不被任何程序接收。广播机制最大的特点就是发送方并不关心接收方是否接到数据,也不关心接收方是如何处理数据的。
Android中广播的是操作系统中产生的各种各样的事件。例如,收到一条短信就会产生一个收到短信息的事件。而Android操作系统一旦内部产生了这些事件,就会向所有的广播接收器对象来广播这些事件。
3.BroadCast Receiver广播接收器
3.1 广播的类型
1.标准广播
标准关闭时一种完全一步执行的广播,假设有很多个广播接收器,当广播发出去的时候,所有的接收器,同时接收到广播消息。接收器之间没有任何先后顺序。这种广播的频率比较高,这也意味着它无法被截断。标准广播的工作流程如下图所示:
2.有序广播
这是一种同步执行的广播,广播接收器有先后之分,同一时刻只有一个接收器能够接收广播,而且,当优先级靠前的接收器没有接收到广播的时候,优先级靠后的接收器就无法接收到广播了。而且优先级高的广播还能截断广播。有序广播的工作流程如下图所示:
3.2 广播接收器的注册
1.静态注册
静态注册方式是在AndroidManifest.xml的application里面定义receiver并设置要接收的action。如果在清单配置文件中配置了广播接收器,那么程序在安装后会自动注册广播接收器。
静态注册方式的特点:不管该应用程序是否处于活动状态,都会进行监听。程序即便未启用,也可以接收广播。
实现静态注册的步骤:
1.创建一个类BootBroadCastReceiver继承BroadcastReceiver类,通过Android Studio创建,可以通过File->New->Other->Broadcast Receiver 这样的快捷方式创建广播接收器,AndroidManifest.xml文件会自动注册。有一个<receiver> 标签对。在onReceiver()中的方法很简单,Toast一个就好了
public class BootBroadCastReceiver extends BroadcastReceiver {
private static final String TAG = "BootBroadCastReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "启动Broadcast", Toast.LENGTH_SHORT).show();
}
}
2.因为我们是需要接收广播信息,所以需要权限RECEIVE_BOOT_COMPLETED。由于Android在启动的时候会发出一条值为android.intent.action.BOOT_COMPLETED的广播,所以 在<receiver>标签对里面添加相应的activity.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.demo.broadcastreceiverdemo">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".BootBroadCastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
</manifest>
3.重启模拟器,程序就可以接到开机广播了。
2.动态注册
动态注册也叫做代码注册,不需要在AndroidManifest.xml中注册。而是在activity里面调用上下文对象的registerReceiver() 方法来注册。和静态的内容差不多。一个形参是receiver对象,另一个是IntentFilter对象。而IntentFilter构造方法的参数是要接收的action。
动态注册方式特点:在代码中进行注册后,当应用程序关闭后,就不再进行监听。
实现动态注册的步骤,监听网络状态:
1.创建一个类MyReceiver继承BroadCast Receiver类,重写父类的onReceiver()方法,这样当网络发生变化的时候,onReceiver的方法就会得到执行。
class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "网络连接状态发生改变", Toast.LENGTH_SHORT).show();
}
}
2.在onCreate()方法中创建一个MyReceiver实例,并创建一个IntentFilter的过滤器,指定action,因为当网络状态发生改变的时候,系统会发送android . net . conn . CONNECTIVITY _CHANGE这样的广播。如果想调用别的关闭可以使用相应的action。最后在调用registerReceiver()方法,将myReceiver和intentFilter的实例传进去,这样就完成了注册。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyReceiver myReceiver = new MyReceiver();
//创建过滤器,并制定action,使之用于接收同action的广播
IntentFilter intentFilter=new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
//注册广播接收器
registerReceiver(myReceiver,intentFilter);
}
//创建过滤器的第二种写法
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
3.最后在onDestory()的方法中调用 unregisterReceiver(),销毁广播,释放内存
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver);
}
4.因为Android 系统为了保护用户设备的隐私,所以对于一些相对于用户来说的敏感权限需要在配置AndroidManifest.xml文件中添加权限声明。
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
3.3发送自定义广播
因为我们知道我们所适应的实际既是广播接收器,也是广播发送器。如果只能接,不能发,那就和收音机没啥区别了。前面已经介绍了,广播主要分为两种类型:标准广播和有序广播。这里介绍一下自定义的广播,相信你看完之后会很兴奋的,你可以定制你的Style了。
3.3.1 发送标准广播
在思考发送广播之前,我们需要有一个接收器,这样发出去的东西,才能知道它到底有没有被接收到。否则只管发了,发了有没有结果不知道,这就做了无用功了。注册的方式,就选择静态注册,因为我们是自定义的广播,所以,系统启动的时候,不会直接发送广播。
具体步骤:
1.创建一个类MyBroadCastReceiver继承 BroadcastReceiver。这里就提示性的弹窗。
public class MyBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "快给我发过来啊,广播", Toast.LENGTH_SHORT).show();
}
}
2.在Androidmanifest.xml文件中,修改receiver标签,添加需要发送的广播。
<receiver
android:name=".myStyle.MyBroadCastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.demo.broadcastreceiverdemo.MY_BROADCAST" />
</intent-filter>
</receiver>
这里需要说明一下,我们在action标签里面添加了一个com.demo.broadcastreceiverdemo. MY_BROADCAST的广播。因此待会发送广播的时候,我们就需要发送这样的广播。然后在receiver里面接收同样的广播标签。特别说明一下,格式是全包名+自定义。要不然会报错。你可以试试。
3.修改activity_main2.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
<Button
android:id="@+id/button"
android:layout_width="150dp"
android:layout_height="50dp"
android:text="发送广播"/>
</LinearLayout>
4.发送广播
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Button button= (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent("com.demo.broadcastreceiverdemo.MY_BROADCAST");
sendBroadcast(intent);
}
});
}
}
我们把按钮的点击事件加入发送自定义广播。首先构建Intent对象,然后把要发送的广播传入,然后调用Context的sendBroadCast()的方法,这样所有监听com.demo.broadcastreceiverdemo. MY_BROADCAST的接收器就会接收到这条广播信息。运行效果如图所示:
这是一个简单的小例子,我们通过在MainActivity中通过触发点击事件,来发送广播,并且自定义接收器,接收广播。
3.3.2 发送有序广播
在开篇的广播介绍中,我们知道有序广播的原理,应用场景。我们知道广播是可以跨进程进行通信的。因此在不同进程之间进行通信是可以实现的。为了测试有序广播我们再新建一个project,然后在从第一个project开始发送广播,第二个project实现接受。
实现步骤:
1.新建一个project ,然后新建一个类SecondBroadCastReceiver继承BroadCastReceiver.
public class SecondBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "第二个进程接收广播", Toast.LENGTH_SHORT).show();
}
2.给第二个AndroidMainifest.xml文件中,修改receiver方法,设置action接受的广播与第一个project的一样com.demo.broadcastreceiverdemo.MY_BROADCAST。
<receiver
android:name=".SecondBroadCastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter >
<action android:name="com.demo.broadcastreceiverdemo.MY_BROADCAST"/>
</intent-filter>
</receiver>
我们可以看到第一个和第一个project都可以接收同样的广播com.demo.broadcastreceiverdemo. MY_BROADCAST。然后回到第一个project界面,运行结果如下图所示:
我们可以看到,我们接受了两次信息,分别为MyBroadCastReceiver和SecondBroadCastReceiver,所以我们的应用程序之间可以被通信。
不过到目前为止,我们的程序里发出的都是标准广播,哪有序广播呢?回到第一个project项目。
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Button button= (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent("com.demo.broadcastreceiverdemo.MY_BROADCAST");
// sendBroadcast(intent);
sendOrderedBroadcast(intent,null);
}
});
}
}
修改intent的发送方法, sendOrderedBroadcast()。 这个方法接收两个参数,第一个参数为intent,,第二个参数是一个与权限相关的字符串。这里传个空值就可以了。然后重新运行第一个project,发现两个应用程序还是可以接收广播。
看上去好像没有什么区别,但是我们知道有序广播,是串行广播,是有优先级的,广播可以被拦截的。
1.设置优先级:
<receiver
android:name=".myStyle.MyBroadCastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.demo.broadcastreceiverdemo.MY_BROADCAST" />
</intent-filter>
</receiver>
在 intent-filter 添加 android:priority="100",数字可以自己设置。数字越大,优先级越高。当你将第二个project的 action 的值设置比第一个大,弹窗会第二个较第一个早。
2.拦截广播
public class MyBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "快给我发过来啊,广播", Toast.LENGTH_SHORT).show();
abortBroadcast();//拦截广播
}
}
在重写的onReceive()的方法里面加上 abortBroadcast()方法,这样就完成了拦截广播的操作。
4.使用本地广播
我们之前讲的广播无论是发送还是接收全部都属于系统全局广播,发出的广播可以被任何应用程序接收,而且也可以接收任何应用程序的广播,所以这就会引发安全问题。通过广播发送的信息被其他程序截获了,或者别的程序不同的发送垃圾广播给你,那还得了。
所以为了能够解决安全性的问题,Android引入了本地广播消息机制,这样发送的广播消息都只能在本应用程序里面传递,广播接收器只能接收来自本应用的消息,这样就不存在安全性问题了。代码如下:
1.在MainActivity中,定义一个内部类集成BroadcCastReceiver
class LocalBroadCastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "接受本地的广播消息", Toast.LENGTH_SHORT).show();
}
}
2.设置发送的广播和接收的广播。我们这里因为要用到本地广播,所以需要引入LocalBroadcastManager本地广播管理类。通过getInstance()方法获取实例,然后通过本地广播调用sendBroadcast()去发送广播,然后设置过滤器,过滤接收的广播action,最后实例化本地广播接收器,去注册本地广播。
private LocalBroadcastManager localBroadcastManager;
private IntentFilter intentFilter;
private LocalBroadCastReceiver localBroadCastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
//获取实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
Button button= (Button) findViewById(R.id.button3);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent("com.demo.broadcastreceiverdemo.MY_LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);
}
});
intentFilter = new IntentFilter();
intentFilter.addAction("com.demo.broadcastreceiverdemo.MY_LOCAL_BROADCAST");
localBroadCastReceiver = new LocalBroadCastReceiver();
localBroadcastManager.registerReceiver(localBroadCastReceiver,intentFilter);//注册本地广播监听器
}
3.还记得代码注册广播,要销毁
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(localBroadCastReceiver);
}
完整代码如下:
public class Main3Activity extends AppCompatActivity {
private LocalBroadcastManager localBroadcastManager;
private IntentFilter intentFilter;
private LocalBroadCastReceiver localBroadCastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
//获取实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
Button button= (Button) findViewById(R.id.button3);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent("com.demo.broadcastreceiverdemo.MY_LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);
}
});
intentFilter = new IntentFilter();
intentFilter.addAction("com.demo.broadcastreceiverdemo.MY_LOCAL_BROADCAST");
localBroadCastReceiver = new LocalBroadCastReceiver();
localBroadcastManager.registerReceiver(localBroadCastReceiver,intentFilter);//注册本地广播监听器
}
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(localBroadCastReceiver);
}
class LocalBroadCastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "接受本地的广播消息", Toast.LENGTH_SHORT).show();
}
}
}
实现效果如下所示:
最后奉上github地址:https://github.com/wangxin3119/BroadCastDemo