大家好,又见面了,我是你们的朋友全栈君。今天我们来聊聊Android四大组件之一:BroadcastReceiver,翻译成中文就是广播接收者。它的主要作用是接收广播,例如系统开机完成、系统电量不足以及系统收到短信等。当我们收到这些广播后,就可以根据需要执行相应的操作。
在现实生活中,发送广播的设备(如电台)、接收广播的设备(如收音机)以及传递信息的媒介(如电磁波)都起到了关键作用。而在Android系统中,这三者的关系也是如此。发送广播的是Broadcast,接收广播的是BroadcastReceiver,而广播之间传递数据的则是Intent。
接下来,我们来看如何注册BroadcastReceiver以接收广播。首先创建一个抽象类BroadcastReceiver,并在其中定义一个抽象方法onReceive(),用于处理接收到的广播。然后创建一个MyBroadcastReceiver类,继承自BroadcastReceiver,并重写onReceive()方法。当接收到广播时,该方法将被调用,并通过intent.getAction()获取到广播的名称。
在Android系统中,BroadcastReceiver有两种注册方式:静态注册和动态注册。静态注册是在AndroidManifest.xml清单文件中进行注册,而动态注册则是在代码中进行注册。
对于需要一直接收某种广播的情况,我们可以使用静态注册方式。以监听手机打电话为例,我们可以在AndroidManifest.xml文件中添加以下代码:
```xml
```
然后在代码中创建一个名为MyPhoneCallReceiver的类,继承自BroadcastReceiver,并重写onReceive()方法。这样,每当有电话拨打时,我们的应用就会收到相应的广播通知。
这段代码是一个广播接收器的声明,它的作用是在应用程序中接收到新的外拨电话时发出通知。在这个例子中,它使用了`android.permission.PROCESS_OUTGOING_CALLS`权限来允许应用程序访问其他应用程序的电话服务。
如果您想要在开机后自动启动应用程序,您可以使用Android的自启动管理器。要使用自启动管理器,请按照以下步骤操作:
1. 在AndroidManifest.xml文件中添加以下代码:
```
```
2. 在您的应用程序中创建一个BroadcastReceiver,并在其中注册要启动的Activity或Service。例如:
```
public class MyBootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
Intent myIntent = new Intent(context, MyActivity.class);
myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(myIntent);
}
}
}
```
3. 在AndroidManifest.xml文件中添加以下代码:
```
```
您好,这可能是因为您的广播接收者静态注册方式与静态注册方式差异。上面例子中MyBroadcastReceiver使用静态注册监听用户打电话,使用动态注册监听用户屏幕点亮与关闭。所以,监听到屏幕的开关只有在APP运行的状态才可以,但是监听打电话的状态无论此时app是否在运行,都可以监听到 。
以下是对您提供内容的重构,并保持段落结构:
静态注册和动态注册:
静态注册依附于清单文件,只要APP启动过一次,所静态注册的广播就会生效,无论当前的APP处于停止使用还是正在使用状态。只要相应的广播事件发生,系统就会遍历所有的清单文件,通知相应的广播接收者接收广播,然后调用广播接收者的onReceiver方法。而动态注册方式依赖于所注册的组件,当APP关闭后,组件对象都不在了动态注册的代码都不存在了,所动态注册监听的action自然不在生效。静态注册的广播传播速度要远远慢于动态注册的广播。需要注意的是,动态注册的广播的优先级大于静态注册的广播。至于这个是为什么呢?额(⊙o⊙)...谷歌写的源代码的时候先对动态广播进行处理然后在对静态广播进行处理。后面我们了解到广播的优先级后会实例证明的。
BroadcastReceiver分类:
广播的发送,可以分为有序广播、无序广播、本地广播以及sticky广播。
有序广播:
有序广播是一种分先后广播接收器的广播,广播接收者的优先级越高,越先接收广播。优先级高的广播先收到广播,收到广播后可以修改广播的内容,也可以拦截广播不让广播向下传递。例如皇上通知知府每人赏金100两,知府通知知县每人赏金100两,最后才是农民知道了赏金的事。一旦知府或者知县不告诉下级赏金的事,那么农民就不知道赏金的事了,这就是有序广播的拦截广播;当然知府或者知县也可以向下级通知只有赏金10两的事,这就是有序广播的修改广播内容。
无序广播:
无序广播指所有与之匹配的广播接收者都能收到广播,没有先后顺序,直到没有广播接收者接收广播为止才会停止广播的传递。例如皇上贴告示,昭告天下每人赏金100两银子一样,那么所有的农民都知道了这件事,没有先后之分。当农民知道钱的事情之后这件事就算了结了。
在之前的介绍中,我们知道当有广播发送时,系统会遍历所有应用程序的接收器。为了防止本应用程序的广播受到外来广播的影响,可以在接收器节点添加android:exported属性并设置为“false”。这样,在系统遍历所有应用程序清单文件中的广播接收者时,将不会对本接收器进行判断和处理。这个值为FALSE表示该接收器与其他应用程序是相互隔离的。
本地广播
与有序和无序广播的全局广播(任何一方发出广播,本手机的任何一个程序都能收到对应的广播)相比,本地广播是局部的广播,基于本程序的广播。其他程序无法收到这个广播。本地广播就像是一个地方官员单独给当地农民发了一两银子,只有当地的人才知道这件事,其他人并不知道。这个广播是在API 21的V4包中新增的,用于确保广播的独家私密性。
这种广播是安全的,外界不会干扰它,同时广播也不会被其他进程接收到。
Sticky广播
Sticky意为粘性。这种广播通常不会终止,只要有一个符合条件的广播接收者能够接收到广播,就会向其发送广播。除非某个广播接收者告诉它停止发送广播,否则永远不会停止发送广播。
发送自定义广播
实例演练:创建两个广播接收者:ZhiFuReceiver/ZhiXianReceiver
创建:
```java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class ZhiXianReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("hui", "ZhiXianReceiver = " + intent.getStringExtra("qian")); //取出广播中携带的数据,因为我存数据的时候是intent.putExtra("qian", "100");存入的。遵循如何存如何取得原则取数据
}
}
public class ZhiFuReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("hui", "ZhiFuReceiver = " + intent.getStringExtra("qian"));
}
}
```
在Android系统中,广播接收器的注册顺序对于接收无序广播是无关紧要的。无论你按照什么顺序注册广播接收器,只要它们实现了正确的BroadcastReceiver接口并且正确处理了指定的action,都可以接收到同一个广播。
然而,对于有序广播,发送顺序就变得非常重要了。Android系统要求发送有序广播的应用程序按照注册顺序依次发送广播。这样一来,只有当前一个应用程序发送完广播后,下一个应用程序才能接收到这个广播。如果两个或更多的应用程序同时尝试发送有序广播,那么只有第一个应用程序能够成功发送广播。这就是为什么我们在注册广播接收器时需要确保我们的应用在清单文件中的顺序与我们期望的接收顺序一致。
下面是如何发送有序广播的示例:
```java
public void sendOrderedBroadcast(View view){
Intent intent = new Intent("my.broadcast.faqian");//action是my.broadcast.faqian,清单文件中的action与之一致方可收到广播
intent.putExtra("qian", "100");//广播中携带的数据
ComponentName receiver = new ComponentName("com.example.yourapp", "com.example.yourapp.ZhiXianReceiver");//这里填写你的接收者的全类名
intent.setComponent(receiver);
sendOrderedBroadcast(intent);//发送有序广播
}
```
注意,你需要将"com.example.yourapp"和"com.example.yourapp.ZhiXianReceiver"替换为你自己的包名和全类名。
public void sendCustomBroadcast(View view) {
Intent intent = new Intent("my.broadcast.faqian"); // action是my.broadcast.faqian,清单文件中的action与之一致方可收到广播
intent.putExtra("qian", "100"); // 广播中携带的数据
/**
* sendOrderedBroadcast(Intent intent, String receiverPermission);
*/
sendOrderedBroadcast(intent, null); // 发送有序广播
}
// 在清单文件中配置接收器和优先级:
注意:在清单文件中设置了两个接收器,分别为ZhiFuReceiver和ZhiXianReceiver,并为它们分别设置了不同的优先级。由于ZhiXianReceiver的优先级大于ZhiFuReceiver的优先级,因此ZhiXianReceiver会先收到广播。
sendOrderedBroadcast的另一个重载方法定义如下:
```java
public void sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
```
参数解释:
1. `intent`:封装了action及其他数据。
2. `receiverPermission`:广播接收者需要的权限。
3. `resultReceiver`:有序广播是支持拦截的,一旦被拦截可以修改广播中数据甚至直接终止广播,这个resultReceiver表示无论当广播传播结束以后我任然会受到广播。(下面会有栗子演示)
4. `scheduler`:用于发送广播的Handler。
5. `initialCode`:发送广播的时候默认携带的数据。
6. `initialData`:发送广播的时候默认携带的数据。
实例:将上面例子中的发送广播的方法修改如下:
```java
Intent intent = new Intent("com.example.MY_BROADCAST");
intent.putExtra("data", "Hello World!");
String receiverPermission = android.Manifest.permission.RECEIVE_BOOT_COMPLETED;
BroadcastReceiver resultReceiver = new MyBroadcastReceiver();
Handler scheduler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
Toast.makeText(MainActivity.this, "收到有序广播消息", Toast.LENGTH_SHORT).show();
}
}
};
int initialCode = 100;
String initialData = "Initial Data";
Bundle initialExtras = new Bundle();
initialExtras.putString("extra_key", "extra_value");
sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras);
```
```java
public class ZhiFuReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String qian = intent.getStringExtra("qian"); //从广播中获取携带的数据
int initCode = intent.getIntExtra("initCode", 0); //从广播中获取携带的数据
String initData = intent.getStringExtra("initData"); //从广播中获取携带的数据
Bundle bundle = intent.getBundleExtra("bundle"); //从广播中获取携带的数据
//处理接收到的广播数据
}
}
```
public class ZhiXianReceiver extends BroadcastReceiver {
/***********获取数据*************/
@Override
public void onReceive(Context context, Intent intent) {
int initCode = getResultCode(); //获取传递过来的initCode
String initData = getResultData(); //获取传递过来的initData
Bundle initBundle = getResultExtras(true); //获取传递过来的Bundle
Log.d("hui", "ZhiXianReceiver = " + "initCode = " + initCode + " ,initdata = " + initData + " ,bundle = " + initBundle.getString("qian"));
}
}
public class ZhiFuReceiver extends BroadcastReceiver {
/***********获取数据*************/
@Override
public void onReceive(Context context, Intent intent) {
int initCode = getResultCode(); //获取传递过来的initCode
String initData = getResultData(); //获取传递过来的initData
Bundle initBundle = getResultExtras(true); //获取传递过来的Bundle
Log.d("hui", "ZhiFuReceiver = " + "initCode = " + initCode + " ,initdata = " + initData + " ,bundle = " + initBundle.getString("qian"));
}
}
结果:
ZhiXianReceiver 的 initCode 为 666,initdata 为 "我是initialData",bundle 为 100。
ZhiFuReceiver 的 initCode 为 666,initdata 为 "我是initialData",bundle 为 100。
有一点需要说明,这里 ZhiFuReceiver 收到了两次数据。为什么呢?因为 ZhiXianReceiver 的优先级大于 ZhiFuReceiver,同时 ZhiXianReceiver 未拦截广播,所以会先收到 ZhiXianReceiver 的数据一次,然后再收到 ZhiFuReceiver 的数据一次。而发送广播的时候声明了 ZhiFuReceiver 为最终接受者,所以无论它曾经有没有收到广播都会再次收到广播。
图示:
下面我们看看拦截后会有什么效果。
有序广播的拦截与修改数据
拦截广播
将上面例子中的 ZhiXianReceiver 添加一行拦截广播的代码,看看结果。
public class ZhiXianReceiver extends BroadcastReceiver {
/***********获取数据*************/
@Override
public void onReceive(Context context, Intent intent) {
int initCode = getResultCode(); //获取传递过来的initCode
String initData = getResultData(); //获取传递过来的initData
Bundle initBundle = getResultExtras(true); //获取传递过来的Bundle
Log.d("hui", "ZhiXianReceiver = " + "initCode = " + initCode + " ,initdata = " + initData + " ,bundle = " + initBundle.getString("qian"));
abortBroadcast(); //拦截广播,广播被终止,以后不会有其他广播接收者再收到广播了。
}
}
这里`abortBroadcast()`拦截了有序广播,不是说每人能再收到广播了么?为什么`ZhiFuReceiver`还能收到广播呢?这是因为`ZhiFuReceiver`是广播的最终接受者,广播从优先级高的广播接收者优先接收,一层一层向优先级较低的传送。当广播被拦截后,广播部分的层层发送这里链路发送完毕,但是有最终广播接收者,故最终广播接收者会收到最后的广播。故`ZhiFuReceiver`会收到广播。
下图理解:
public class ZhiXianReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
/***********获取数据*************/
int initCode = getResultCode();//获取传递过来的initCode
String initData = getResultData();//获取传递过来的initData
Bundle initBundle = getResultExtras(true);//获取传递过来的Bundle
Log.d("hui", "ZhiXianReceiver = " + "initCode = " + initCode + " ,initdata = " + initData + " ,bundle = " + initBundle.getString("qian"));
/*************修改数据****************/
setResultCode(8989);//修改initCode
setResultData("ZhiXianReceiver修改了数据"); //修改initData
//修改bundle数据
Bundle bundle = new Bundle();
bundle.putString("qian", "10");
setResultExtras(bundle);
}
}
发送本地广播
本地广播的使用是在代码中实现的,因为在发送本地广播时,它是直接在代码中的注册广播列表中进行匹配,从而调用相应的`onReceiver`方法。下面是一个简单的源码示例:
```java
public void sendBroadcastSync(Intent intent) {
if (sendBroadcast(intent)) {
executePendingBroadcasts();
}
}
private void executePendingBroadcasts() {
while (true) {
BroadcastRecord[] brs = null;
synchronized (mReceivers) {
int N = mPendingBroadcasts.size();
if (N <= 0) {
return;
}
brs = new BroadcastRecord[N];
mPendingBroadcasts.toArray(brs);
mPendingBroadcasts.clear();
}
for (int i = 0; i < brs.length; i++) {
BroadcastRecord br = brs[i];
for (int j = 0; j < br.receivers.size(); j++) {
// 在这里直接调用其 onReceiver 方法了
br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
}
}
}
}
```
请根据提供的内容完成内容重构,并保持段落结构:
使用`localBroadcastManager.registerReceiver(myBroadCastReceiver, intentFilter)`进行注册:
```java
/** * 本地广播接收者进行注册,必须在代码中注册,清单文件注册是无效的 */
public void registerMyAPPReceiver(View view) {
//创建广播接收者
MyBroadCastReceiver myBroadCastReceiver = new MyBroadCastReceiver();
MyBroadcastReceiver2 myBroadCastReceiver2 = new MyBroadcastReceiver2();
//封装要接收的广播类型
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("my.broadcast.faqian2");
//拿到LocalBroadcastManager对象,对固定的Receiver进行注册,成为本地广播接收者
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(MainActivity.this);
// 对不同的接收者进行注册
localBroadcastManager.registerReceiver(myBroadCastReceiver, intentFilter);
localBroadcastManager.registerReceiver(myBroadCastReceiver2, intentFilter);
}
```
请注意,以下是重构后的内容:
发送本地广播时,请遵循以下注意事项:
1. registerReceiver注册一个广播接收者可以多次执行,但不建议这样写。
2. 本地广播不能被拦截。
3. registerReceiver对应的还有unregisterReceiver(receiver)。
示例代码:
```java
/** * 发送本地广播 * @param view */
public void sendMyAPPBroadcat(View view){
Intent intent = new Intent("my.broadcast.faqian2"); //action是my.broadcast.faqian,清单文件中的action与之一致方可收到广播
Bundle bundle = new Bundle();
bundle.putString("qian", "100"); //广播中携带的bundle数据
intent.putExtra("bundle_data", bundle);
//使用LocalBroadcastManager发送广播
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcastSync(intent);//发送
}
```
发送sticky广播时,请先添加权限:
```xml
```
示例代码:
```java
/** * 发送sticky本地广播 * @param view */
public void sendStickyMyAPPBroadcat(View view){
Intent intent = new Intent("my.broadcast.faqian2"); //action是my.broadcast.faqian,清单文件中的action与之一致方可收到广播
Bundle bundle = new Bundle();
bundle.putString("qian", "100"); //广播中携带的bundle数据
intent.putExtra("bundle_data", bundle);
//使用LocalBroadcastManager发送广播并设置为sticky模式
LocalBroadcastManager.getInstance(MainActivity.this).sendStickyBroadcast(intent);//发送
}
```
停止使用sticky广播时,请调用removeStickyBroadcast()方法:
```java
/** * 停止使用sticky本地广播 * @param context */
public static void stopStickyMyAPPBroadcat(Context context){
context.stopStickyBroadcast(new Intent("my.broadcast.faqian2")); //移除对应广播的sticky模式
}
```