个人笔记,仅供参考
1 广播简介
- Android提供了一整套完整的API,允许应用程序自由地接收和发送广播
Android中的广播类型主要分两种:
(1)标准广播:
异步、效率高、无先后顺序之分、无法被截取,同一时刻所有的广播接收器会同时收到这条广播
(2)有序广播:
同步、有先后顺序之分、优先级高的先收到,同一时刻只有一个广播接收器收到,且需要执行完逻辑后,再传递给下一个广播接收器,允许截断,那么下一个接收器将无法收到广播
2 接收系统广播
以监听网络为例,新建项目BroadcastDemo
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(MainActivity.this, "网络有变化", Toast.LENGTH_SHORT).show();
}
}
}
- 新建内部类NetWorkChangeReceiver 继承自BroadcastReceiver,并重写onReceive方法,当收到广播后,我们让它弹出一个Toast
- 新建一个IntentFilter实例,并制定它要监听的ACTION为"android.net.conn.CONNECTIVITY_CHANGE",因为当网络条件发生变化的时候,系统会内置发出这一条广播
- 新建NetWorkChangeReceiver实例
- 最后调用registerReceiver方法注册广播接收器,当网络发生变化的时候,我们的接收器会收到广播,并执行onReceive方法
广播接收器的注册方式分为动态注册和静态注册,动态注册是在代码中注册,静态注册是在配置文件AndroidManifest中注册。
- 而动态注册的广播接收器都需要记得在销毁界面的时候取消注册,因此我们在onDestroy方法里调用了unregisterReceiver方法。
当我们切换网络开关的时候,运行效果:
当然,我们可以再进一步优化一下,明确地显示当前是否有网络
- 我们修改一下onReceive方法里的代码
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, "网络已开启", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, "网络已关闭", Toast.LENGTH_SHORT).show();
}
}
}
- 通过getSystemService拿到ConnectivityManager ,这是一个系统服务类,专门用来管理网络
- 调用getActiveNetworkInfo拿到NetworkInfo实例,并通过isAvailable方法来判断当前网络的具体状态
当我们再次切换网络开关的时候,会弹出对应Toast提示,运行效果:
由于我们使用到了网络,因此记得在配置文件AndroidManifest中加入权限
注意:
我们之前的广播接收器中onReceive方法都只是简单的弹出Toast,当真正在项目中用到的时候,我们可以编写自己需要的逻辑
需要注意的是:
不要在onReceive方法中添加过多的逻辑和耗时操作
不允许开启线程
广播接收器更多扮演的是一种打开程序其他组件的角色,如创建一条通知栏、启动一个服务等等。
3 发送自定义广播
3-1 发送标准广播
- 首先新建一个广播接收器,用于接收我们等下要发送的广播
public class MyBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "收到来自MyBroadcastReceiver的广播", Toast.LENGTH_SHORT).show();
}
}
- 然后注册广播接收器,上一节我们用的是动态注册,这里我们用静态注册的方式,打开配置文件AndroidManifest,在 内声明我们的BroadcastReceiver;里面指定intentFilter的Action,这个值可以自定义,这里我们就用包名和一个大写字符。代表这个接收器只接受含有这条信息的广播。
- 接着,我们在主界面MainActivity里加入一个button作为发送广播的触发点,点击后利用intent指定我们要发送的广播的值,最后调用sendBroadcast方法,就发送了一条标准的广播
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1 = findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.broadcastdemo.MY_BROADCAST");
sendBroadcast(intent);
}
});
}
}
可以看到此时我们的广播接收器已经接收到了发出的广播,运行效果:
3-2 发送有序广播
广播可以跨进程,也就是说应用程序发出的广播,其他应用程序可以收到
我们再新建一个BroadcastDemo2项目,并同样新建另一个广播接收器AnotherBroadcastReceiver
public class AnotherBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "收到来自AnotherBroadcastReceiver的广播", Toast.LENGTH_SHORT).show();
}
}
- 而且同样指定它监听的action为"com.example.broadcastdemo.MY_BROADCAST"
- 当我们点击第一个Demo中的按钮时,此时第二个Demo中的广播接收器AnotherBroadcastReceiver同样能够接收到广播(此处图略)
这足以说明广播可以跨进程。
我们再来看一下怎么发送有序广播?
- 很简单,只需要更改一行代码即可,sendOrderedBroadcast第二个字符是与权限相关的,这里设置成null即可
Intent intent = new Intent("com.example.broadcastdemo.MY_BROADCAST");
// sendBroadcast(intent);
sendOrderedBroadcast(intent,null);
既然是有序的广播,那么怎么知道谁先收到广播呢?
- 我们可以给广播接收器设置优先级,比如给AnotherBroadcastReceiver设置
android:priority="100",以保证它优先收到广播
那么优先收到后,又如何截断呢?
- 只需要在AnotherBroadcastReceiver的onReceive方法里调用abortBroadcast即可
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "收到来自MyBroadcastReceiver的广播", Toast.LENGTH_SHORT).show();\
abortBroadcast();
}
- 此时,若再次点击发送广播,AnotherBroadcastReceiver会优先收到,并采取截断,广播则不会再继续传递下去,MyBroadcastReceiver将收不到
4 本地广播
前面使用的广播都属于全局广播,也就是说发出的广播可以被其他任何应用程序收到,也同样可以接收到任何应用程序的广播,这样就容易引起安全性问题。我们发出的广播可能会被截取,又或者收到各种垃圾广播。
为了解决这个问题,Android拥有一套自带的本地广播机制。
发出的广播,只能在本程序内部进行传递
而广播接收器,只能接收来自本程序的广播。
直接看代码:
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private LocalBroadcastManager localBroadcastManager;
private LocalReceiver localReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
Button button1 = findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.broadcastdemo.LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);//发送本地广播
}
});
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.broadcastdemo.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, "收到本地广播", Toast.LENGTH_SHORT).show();
}
}
}
- 本地广播主要使用了一个LocalBroadcastManager来对广播进行管理,并提供了发送和接收的方法,我们需要首先获取它的实例localBroadcastManager
- 首先新建广播接收器LocalReceiver,用于接收本地广播
- 使用intentFilter指定其Action,而此时动态注册,采用的是localBroadcastManager调用registerReceiver,代表注册为本地广播接收器
- 发送本地广播,只需要由localBroadcastManager调用sendBroadcast即可
- 最后别忘了取消注册
运行效果:
5 广播实践
这里简单讲讲目前我工作中遇到过的常见使用情况:
一般发送广播可以用于不那么紧急的UI更新操作
比如:
活动A——> 活动B ——> 活动C
- 我在C界面给某个用户点了赞
活动A<—— 活动B <—— 活动C
- 当我一路回退到活动A的时候,此时活动A中此用户的状态肯定需要变为已赞对吧?
- 而每次回退都刷新发请求是不合适的
- 因此可以在C界面的时候,就简单发送一个广播,当活动A的onResume接收到的时候,改变该用户的状态