Broadcast-Receiver的使用
最近在复习Android的知识,从最基础的开始深入探究,初学Android的时候,四大组件学的很茫然,其实最多的用到的也就是Activity,其他的接触的都不多。
这里我们探究一下广播机制。
1.动态注册
我们用As新建一个接收器。
这里我们可以使用它的快捷新建的方式,右键包名新建一个Other,选择Broadcast Receiver即可快速新建接收器。
我们输入类名,下面两个默认选中即可,其中exported选中为true,标识允许其他APP调用该组件,enabled标识是否可以被系统实例化,选中也为true,不必多探究。
确认之后,就可以看到系统为我们生成了一段代码1
2
3
4
5
6
7public class MyReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
//继承了BroadcastReceiver ,需要实现其中的接受方法。
}
}
同时我们的manifest文件中也出现了注册,注意一下四大组件都需要在manifest中定义哦。
1 | <receiver |
接收器中我们写一个Log,修改如下1
2
3
4
5
6
7
8public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//这里我封装了一下log,你依然可以使用原生的log来打印
Logs.d("收到!");
}
}
这样我们的接收器就写好了。
我们在Activity中注册接收器,首先生成接收器和过滤器对象,接收器是用于注册的,那么过滤器是用来干嘛的?
机智如你,当然是过滤广播的,举个例子,学校的广播分年级宣布事情,你是高一的,你就不需要管高二的事情,而只需要过滤出高一的事情即可,一会你就会看到IntentFilter 设置所关心的事情的方法。
1 | private MyReceiver myReceiver = new MyReceiver(); |
接下来注册广播,在onCreate()里设置你的关心并且注册。1
2
3
4
5
6
7
8@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mIntentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//注册广播接收器
registerReceiver(myReceiver,mIntentFilter);
}
可以看到,addAction()用于设置关心,而我设置的android.net.conn.CONNECTIVITY_CHANGE属性是代表了网络连接状态,所以你自会明白,我这个接收器,专门接受网络变化的广播。
最后要是离开这个页面,我们还需要解注册1
2
3
4
5
6@Override
protected void onDestroy() {
super.onDestroy();
//解注册
unregisterReceiver(myReceiver);
}
运行之后,界面没有变化,而当我们点击数据或者无线网关掉网络,我们的Log就会出现打印。
2. 静态注册
上面部分叫做动态注册,因为必须要在页面中使用才能看出来效果,那么还有一个静态注册,场景也很广泛,不用打开APP即可实现。
比如我们常见的开机自启动。
当系统开机的时候,发送一条广播。
注册开机广播接收的App在接收器中即可收到这条广播,然后在其中的逻辑中运行它的服务,即可实现开机自启动。
但目前很多国产手机都右禁用开机自启动,所以这个过程要更为复杂一些。
依然用刚才的接收器,里面的内容改成Toast(这里依然是我封装的,请自己选择合适的方式使用)1
2
3
4@Override
public void onReceive(Context context, Intent intent) {
Toasts("开机啦!");
}
然后给系统添加开机自启动权限。1
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
接着又修改了一下我们刚才注册的receiver,里面添加了
因为我们刚才学到的动态注册就是用了这个过滤器,这里,只不过是在xml中的写法,比如下面的action标签,添加的android.intent.action.BOOT_COMPLETED属性,都是我们刚才降到过的,其中BOOT_COMPLETED标识开机完成,类似的属性还有重启等等。1
2
3
4
5
6
7
8<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
这样我们只需要一个接收器和xml这一段内容即可,刚才写的动态注册都要删掉了。
这样我们关机后,再开机即可收到Toast,当然部分手机可能因为自动禁用开机自启动权限效果有所不同,请自行探究。
3.自定义广播
有了刚才的经验,我们就可以发送自定义广播了,无非是过滤器所关注的广播改成你所广播的内容,(接收器目前是开机Toast提示,该与不该都可),主要是看xml中,1
2
3
4
5
6
7
8
9<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="susu.MY_BROADCAST"/>
</intent-filter>
</receiver>
我继续在原代码上添加了新的一行,并且让这个关注点叫做susu.MY_BROADCAST,名字随意,一会我们发送的时候对应起来即可。
MainActivity中添加了一个按钮,点击这个按钮的时候即可发送广播,可以看到这里的广播就是我们刚才注册的那个。1
2
3
4
5
6
7
8button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("susu.MY_BROADCAST");
sendBroadcast(intent);
}
});
我们看一下我们调用的这个intent构造吧,(按住Ctrl,单击new Intent的Intent)1
2
3
4// @param action The Intent action, such as ACTION_VIEW.
public Intent(String action) {
setAction(action);
}
可以看到@param已做出解释,action是intent action,也就是我们前面一直提到的action,这样就全部对应起来啦。
运行起来,点击按钮,会弹出相应提示。
4. 小结
这里总结一下。
- 广播注册主要分为静态注册和动态注册,其中值得我们关注的就是过滤器,通过设置过滤的action(动作),接受自己所感兴趣的事情。
- 上述例子中,开机自启动的实现如果不能达成效果也很正常,由于API的变更或者厂商的定制总会出现一些各种各样的问题。
3.前面提起过,广播是全局的,你写的App发送广播,别的App也可以收到(如果注册了的话),别的App发你的也可以收到,这里你可以写两个Demo尝试一下。 - 如果你用的Android8.0及以上的系统,可能上述自定义广播栗子无法实现预期效果,主要是因为Android API的修改,Android系统电量系统优化导致的。
你可以给intent增加了一个内容,修改如下1
2
3Intent intent = new Intent("susu.MY_BROADCAST");
intent.setComponent(new ComponentName("cn.surine.statusbardemo","cn.surine.statusbardemo.MyReceiver"));
sendBroadcast(intent);
我们可以看到设置了一个Component,第一个参数是包名,第二个参数是MyReceiver即接收器的路径。
酱紫,就可以收到啦!
你以为结束了?
不可能!!!
5.有序广播
What?怎么又来,有序广播是什么。
有序广播是按照顺序来的,举个栗子,小红,小张,小明都要接受老师的同一条命令。
有两种形式,第一种,老师把他们三个找来,一下子传达,这个方式叫做标准广播,上面我们写过的都是标准广播
第二种,老师把小红找来,传达之后让小红传给小张,小张传给小明,但这里就有问题了,为什么是先传给小红而不是小明,小张会不会不给小明传达?等等等等问题。
这种方式就是有序广播,对应的问题就是广播优先级和广播截断
那么现在我们实现有序广播。1
sendOrderedBroadcast(intent,null);
好本次教程到此结束。
What???
因为太简单了,刚才我们写的sendBroadcast方法你还记得吧,别告诉我你忘了,你要是忘了就代表你一直在抄我的代码而没有自己写。
那个方法传了一个intent进去,这里sendOrderedBroadcast也传了intent,第二个参数为null是receiverPermission,暂且不用管。
这样就可以了,这就是有序广播。
但是有序广播刚才提到了两个问题,优先级和截断。
优先级在哪里设置?
答案就是注册这里,设置优先级100,保证先收到。1
2
3
4
5
6
7
8
9<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter android:priority="100">
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="susu.MY_BROADCAST"/>
</intent-filter>
</receiver>
确保它第一个收到之后,我们可以在它的接收器方法中增加一个截断。1
2
3
4@Override
public void onReceive(Context context, Intent intent) {
abortBroadcast();
}
这样 下一个,比如说小明,就收不到广播了,是不是很霸道的样子。
这里我就一个Demo,我也木有演示效果,你可以自行演示。
6. 本地广播
刚才我们上面全部的广播,我都提到过,全局,全局意味着什么,别的App也可以收到我们的广播,万一我们的广播action恰好被别人知道了(虽然几率很小),那他是不是可以不停的发送垃圾广播来干扰我们的app正常运行,或者我们发送的关键数据,会不会泄露到其他App中?
这系列问题,都急需一个方法来处理,这就是本地广播。
下面我们来实现。
依然是MyReceive,我们看一下新代码,自行处理接受到信息的内容。1
2
3
4
5
6
7public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toasts.show("接收到广播");
}
}
xml改成这样。1
2
3
4<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true" />
所有的操作都将在Java里面匹配。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49package cn.surine.statusbardemo;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button button;
private LocalBroadcastManager localBroadcaseManager;
private MyReceiver myReceiver = new MyReceiver();
private IntentFilter intentFilter = new IntentFilter();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取LocalBroadcastManager实例
localBroadcaseManager = LocalBroadcastManager.getInstance(this);
//添加过滤器
intentFilter.addAction("susu.MY_BROADCAST");
//注册
localBroadcaseManager.registerReceiver(myReceiver,intentFilter);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//发送广播
Intent intent = new Intent("susu.MY_BROADCAST");
intent.setComponent(new ComponentName("cn.surine.statusbardemo","cn.surine.statusbardemo.MyReceiver"));
sendBroadcast(intent);
}
});
}
//解注册
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcaseManager.unregisterReceiver(myReceiver);
}
}
这里是发送的全部的代码,可以看到似曾相识,因为这个和动态注册是一样的,只不过由LocalBroadcastManager一手接管了注册,解注册等等工作。
可能你有疑问,动态和本地都差不多,为啥要区分,
主要是是因为动态广播在其他App可以发送,本地广播就局限于App内。
7 .总结
又是总结。
这回真的是总结了。
在这里开始挖坑,有的童鞋可能用过EventBus,它的形式跟广播类似,不用在乎是否收到,只要订阅了接收器,就可以收到接受的信息,不过也有所不同。
挖好了坑,我们不知道何年何月才能探究出来。
总之,要学的东西有很多,继续加油!