Android开发--Lesson07--广播机制
定义
Android广播机制是Android系统中的一种消息传递机制,允许应用程序之间通过发送和接收广播来实现通信。它类似于发布/订阅模式,其中广播发送者不指定接收者,而是将消息(即广播)发送给操作系统,然后由操作系统通知所有感兴趣的接收者。
特点:
- 解耦性:广播发送者和接收者彼此不了解对方的存在,这降低了组件之间的耦合度。
- 异步性:广播的发送和处理是异步进行的,这意味着广播发送后立即返回,不会阻塞发送者的操作。
- 灵活性:一个广播可以被多个接收者接收,同样一个接收者也可以监听多种广播。
功能:
- 系统广播:Android系统会根据不同的事件发送广播,如电池电量变化、网络连接状态改变等。应用可以注册接收这些广播来响应特定的系统事件。
- 自定义广播:应用可以自己发送自定义广播,用于内部组件间的通信或者与其他应用共享信息。
- 有序广播:广播按照优先级顺序传递给接收者,高优先级的接收者可以先接收到广播,并且可以选择终止广播继续传递或修改广播内容后再传递给下一个接收者。
在使用广播机制时,开发者需要在应用的AndroidManifest.xml
文件中声明广播接收者(BroadcastReceiver),并根据需要设置过滤器来指明该接收者感兴趣的广播类型。此外,从Android 8.0(API级别26)开始,为了提高性能和用户体验,对后台应用接收广播的能力进行了限制,因此一些广播只能由前台应用接收或是需要通过显式注册的方式才能接收。
由于我们现在的开发环境都是基于Android10以上的了,故而注册广播的方式就统一使用动态注册的方式
创建广播接收者
在Android中,广播接收者(BroadcastReceiver)是一种用于接收并处理广播消息的组件。通过广播接收者,应用程序可以监听来自系统或者其他应用发出的广播信息,并根据这些信息执行相应的操作。
广播接收者的主要特点:
- 异步接收:广播接收者以异步的方式接收广播消息,这意味着它不会阻塞发送者的操作。
- 解耦设计:广播发送者和接收者之间不需要直接联系,它们之间的通信通过操作系统进行中转。
- 全局或局部广播:广播可以是全局的(应用间通信),也可以是局部的(应用内通信)。对于应用间的通信,广播接收者能够接收到其他应用发送的广播;对于应用内的通信,则可以通过注册本地广播管理器来限制广播只在应用内部传播,从而提高安全性和效率。
如何使用广播接收者:
- 定义广播接收者:需要创建一个继承自
BroadcastReceiver
类的子类,并重写onReceive(Context context, Intent intent)
方法,在这个方法中编写响应广播的具体逻辑。 - 注册广播接收者:有两种方式注册广播接收者,分别是静态注册和动态注册。
- 静态注册:在
AndroidManifest.xml
文件中声明广播接收者,适用于监听一些系统级别的广播。 - 动态注册:在代码中通过
Context.registerReceiver()
方法注册广播接收者,适用于监听应用内的广播或者当应用处于前台时监听特定的广播。
- 静态注册:在
- 处理广播:一旦注册了广播接收者并且有匹配的广播发出时,系统就会调用对应的
onReceive()
方法。
新建一个广播接收者
根据上面的不走创建一个Receiver接收者的模块,也就是广播接收者模块
如上可以Class Name 可以默认,其它的都是默认的,直接点击Finish就好了
之后就会默认创建一个广播接收者,默认的接收者继承于 BroadcastReceiver;有一个需要重写的方法:
public void onReceive(Context context, Intent intent)
此方法就是方广播发送方向自己发送广播后需要执行的操作
系统广播
系统广播是当Android在执行系统事件的时候,有些事件会广播到系统的各个组件;Android系统提供了许多内置的广播(系统广播),这些广播可以被应用程序监听以响应特定的系统事件
Intent.ACTION_AIRPLANE_MODE_CHANGED:飞行模式开启或关闭时发送。
Intent.ACTION_BATTERY_CHANGED:电池状态或电量发生变化时持续发送(这是一个粘性广播)。
Intent.ACTION_BATTERY_LOW:电池电量低时发送。
Intent.ACTION_BATTERY_OKAY:从低电量恢复到正常电量时发送。
Intent.ACTION_BOOT_COMPLETED:设备启动完成后发送,只有已安装的应用才能接收到此广播。
Intent.ACTION_CAMERA_BUTTON:按下相机按钮时发送。
Intent.ACTION_CONFIGURATION_CHANGED:配置变化时发送,如语言、屏幕方向等改变。
Intent.ACTION_DATE_CHANGED:日期改变时发送。
Intent.ACTION_DEVICE_STORAGE_LOW:存储空间不足时发送。
Intent.ACTION_HEADSET_PLUG:插入或拔出耳机时发送。
Intent.ACTION_LOCALE_CHANGED:区域设置变化时发送。
Intent.ACTION_MEDIA_MOUNTED:成功挂载外部介质(如SD卡)时发送。
Intent.ACTION_MEDIA_UNMOUNTED:卸载外部介质时发送。
Intent.ACTION_POWER_CONNECTED:连接了外部电源时发送。
Intent.ACTION_POWER_DISCONNECTED:断开了外部电源时发送。
Intent.ACTION_SCREEN_ON:屏幕打开时发送。
Intent.ACTION_SCREEN_OFF:屏幕关闭时发送。
Intent.ACTION_SHUTDOWN:设备关机前发送。
当然使用还有很多广播事件,比如短信和电话也会有广播事件,,允许应用程序监听这些事件并做出响应。不过,由于隐私和安全性的考虑,从较新的Android版本开始,对访问这些敏感事件进行了更严格的限制。
SMS_RECEIVED_ACTION (android.provider.Telephony.SMS_RECEIVED
):当设备接收到一条新的短信时发送此广播。应用程序可以监听这个广播来获取新短信的通知。
如果用户需要使用监听短信的广播,那么就需要在Android的清单文件中,配置声明:
<uses-feature android:name="android.hardware.telephony" android:required="false" /> <uses-permission android:name="android.permission.RECEIVE_SMS" />
然后显式的在Java代码中完成广播注册,就可以监听系统的短信广播了
案例:监听系统的短信事件,先写Received的实现:
public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i("MyReceiver",intent.getAction()); } }
然后注册监听SMS的广播事件,并且需要向Android系统查询是否可以监听的权限,如果没有就需要注册:
public class MainActivity extends AppCompatActivity { private MyReceiver receiver; @SuppressLint("UnspecifiedRegisterReceiverFlag") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); //高版本权限申请,需要判断是否有SMS的权限,如果没有则需要注册 if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECEIVE_SMS)!= PackageManager.PERMISSION_GRANTED){ //PackageManager.PERMISSION_GRANTED 当前的包管理器接受的权限 ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.RECEIVE_SMS},1); } //动态注册 receiver = new MyReceiver(); //接收 手机信息 android.provider.Telephony.SMS_RECEIVED String action = "android.provider.Telephony.SMS_RECEIVED"; IntentFilter filter = new IntentFilter(); //添加动作,动作就是监听android.provider.Telephony.SMS_RECEIVED事件,就是短信事件 filter.addAction(action); //注册广播,只有注册之后才可以完成对系统的广播监听 registerReceiver(receiver,filter); } @Override protected void onDestroy() { super.onDestroy(); //程序销毁的时候,也取消监听广播 unregisterReceiver(receiver); } }
然后测试给系统发送一条模拟消息,看否可以监听到:
如上的日志输出,成功的监视到了系统的发送信息的广播,然后控制台日志输出了action为SMS_RECEIVED
自定义广播给其它组件
自定义广播是在原来广播监听的基础之上完成的,原来的广播监听者监听的是系统广播,但是现在是自己发送广播给其它组件监听
案例:
public class MainActivity extends AppCompatActivity { private MyReceiver receiver; @SuppressLint("UnspecifiedRegisterReceiverFlag") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); //动态注册 receiver = new MyReceiver(); //监听SEND_Maming 标识符发送的广播 String action = "SEND_Maming"; IntentFilter filter = new IntentFilter(); filter.addAction(action); //注册广播,只有注册之后才可以完成对系统的广播监听 registerReceiver(receiver,filter); //发送广播的事件 Button bt = findViewById(R.id.bt); bt.setOnClickListener(v->{ Intent intent = new Intent(); //需要发送到广播的标识符 intent.setAction("SEND_Maming"); //发送广播 sendBroadcast(intent); }); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(receiver); } }
测试结果,点击发送广播按钮
附件,按钮的XML文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" tools:context=".MainActivity"> <Button android:id="@+id/bt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="发送广播"/> </LinearLayout>
广播的优先级
广播的优先级主要依靠于setPriority();方法设置,它主要是依赖于IntentFilter的方法,也就是意图过滤,设置优先级可以使得发送广播的优先级,越大会越先广播,当然这只能在有序广播中实现
测试案例:
新建三个广播接收者,并且在注册时设置不一样的优先级:
接收者1:
public class MyReceiverOne extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i("MyReceiverOne","MyReceiverOne接收到了广播"); } }
接收者2:
public class MyReceiverTwo extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i("MyReceiverTwo","MyReceiverTwo接收到了广播"); } }
接收者3:
public class MyReceiverThree extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i("MyReceiverThree","MyReceiverThree接收到了广播"); } }
然后即使注册广播监听,设置其优先级:
public class MainActivity extends AppCompatActivity { private MyReceiverOne myReceiverOne; private MyReceiverTwo myReceiverTwo; private MyReceiverThree myReceiverThree; @SuppressLint("UnspecifiedRegisterReceiverFlag") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this); setContentView(R.layout.activity_main); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); return insets; }); //注册第一个广播接收者 myReceiverOne = new MyReceiverOne(); //监听广播的标识符 String action = "SEND_Maming"; //意图过滤 IntentFilter filter1 = new IntentFilter(); //设置优先级 filter1.setPriority(1000); //添加监听动作 filter1.addAction(action); //注册 registerReceiver(myReceiverOne,filter1); //注册第一个广播接收者 myReceiverTwo = new MyReceiverTwo(); //意图过滤 IntentFilter filter2 = new IntentFilter(); //设置优先级 filter2.setPriority(10); //添加监听动作 filter2.addAction(action); //注册 registerReceiver(myReceiverTwo,filter2); myReceiverThree = new MyReceiverThree(); IntentFilter filter3 = new IntentFilter(); filter3.setPriority(100); filter3.addAction(action); registerReceiver(myReceiverThree,filter3); Button bt = findViewById(R.id.bt); bt.setOnClickListener(v->{ Intent intent = new Intent(); intent.setAction("SEND_Maming"); //发送广播 sendOrderedBroadcast(intent,null); }); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(myReceiverTwo); unregisterReceiver(myReceiverOne); unregisterReceiver(myReceiverThree); } }
测试,点击发送广播按钮之后,三个广播接收者会不会监听到:
如上图,测试的One广播接收者先打印,因为其优先级是1000,是最大的
而Three是100第二,而Two则是10,为最小的
故而输出次序不一样,需要注意的是,当两个广播的优先级注册时是一样的时候,优先级由先组成的优先,代码的一次执行,故而写在前面的会先广播
对某个接收者单独广播
在上面的代码不改变的基础之上,只需要修改广播的发送方式
Button bt = findViewById(R.id.bt); bt.setOnClickListener(v->{ Intent intent = new Intent(); intent.setAction("SEND_Maming"); //指定 myReceiverThree 收到 sendOrderedBroadcast(intent,null,myReceiverThree,null,0,null,null); });
如上,就是指定第三个接收者接收到广播:
------END------