当前位置:   article > 正文

Android Studio 学习记录-收发应用广播_android studio 广播

android studio 广播

目录

收发标准广播

1.发送标准广播

2.定义广播接收器

3.开关广播接收器

​编辑​编辑

 收发有序广播

1.发送广播时要注明这是个有序广播

2.定义有序广播的接收器

3.注册有序广播的多个接收器

​编辑​编辑收发静态广播 

 定时管理器 AlarmManager

1.定义定时器的广播接收器

2.开关定时器的广播接收器

3.设置定时器的播报规则

补充: 闹钟延迟的下拉框设置


        本文介绍应用广播的几种收发形式,包括如何收发标准广播、如何收发有序广播、如何收发静态广播、如何监听定时管理器发出的系统闹钟广播等。

收发标准广播

        Android的广播机制借鉴了Wifi的通讯原理,不必搭建专门的通路,就能在发送方与接收方之间建立连接。同时广播(Broadcast)也是Android的四大组件之一,它用于Android各组件之间的灵活通信,与活动的区别在于:

  1. 活动只能一对一通信,而广播可以一对多,一人发送广播,多人接收处理。
  2. 对于发送方来说,广播不需要考虑接收方有没有在工作,接收方在工作就接收广播,不在工作就丢弃广播。
  3. 对于接收方来说,因为可能会接收到格式各样的广播,所以接收方要自行过滤符合条件的广播,之后再解包处理。

        与广播有关的方法主要有已下3个

  • sendBroadcast:发送广播。
  • registerReceiver:注册广播的接收器,可在onStart或onResume方法中注册接收器。
  • unregisterReceiver:注销广播的接收器,可在onStop或onPause方法中注销接收器。

        广播的收发过程可分为3个步骤:发送标准广播、定义广播接收器、开关广播接收器,分别说明如下:

1.发送标准广播

        广播的发送操作很简单,一共只有两步:先创建意图对象,再调用sendBroadcast方法发送广播即可。

  1. public class BroadcastType1 extends AppCompatActivity implements View.OnClickListener{
  2. //这是广播的动作名称,发送广播和接收广播都以它作为接头暗号
  3. private final static String STANDARD_ACTION = "com.example.helloandroid.Broadcast";
  4. private TextView tv_ReceiveBroadcast1;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_broadcast_type1);
  9. findViewById(R.id.btn_SendBroadcast1).setOnClickListener(this);
  10. }
  11. @Override
  12. public void onClick(View v){
  13. if (v.getId()==R.id.btn_SendBroadcast1){
  14. Intent intent = new Intent(STANDARD_ACTION); //创建指定动作的意图
  15. sendBroadcast(intent); //发送标准广播
  16. }
  17. }
  18. }

2.定义广播接收器

        广播发出来后,还得有设备去接收广播,也就是需要广播接收器。接收器主要规定两个事情:一个是接收什么样的广播,另一个是接收到广播以后要做什么。由于接收器的处理逻辑大同小异,因此Android提供了抽象之后的接收器基类BroadcastReceiver,开发者自定义的接收器都从BroadcastReceiver派生而来。新定义的接收器需要重写onReceive方法,方法内部先判断当前广播是否符合代收的广播名称,校验通过后再开展后面的业务逻辑。

  1. private String mDesc ="这里查看标准广播的收听信息";
  2. //定义一个标准广播的接收器
  3. private class StandardReceiver extends BroadcastReceiver {
  4. //一旦收到广播马上触发onReceive方法
  5. @Override
  6. public void onReceive(Context context,Intent intent){
  7. //广播意图飞控且接头暗号正确
  8. if (intent != null && intent.getAction().equals(STANDARD_ACTION)){
  9. mDesc = String.format("%s\n%s收到一个标准广播",mDesc,DateUtil.getNowTime());
  10. tv_ReceiveBroadcast1.setText(mDesc);
  11. }
  12. }
  13. }

3.开关广播接收器

        为了避免资源浪费,还要求合理使用接收器。活动页面启动之后才注册接收器,活动页面停止之后就注销接收器。在注册接收器的时候,允许事先指定只接收某种类型的广播,即通过意图过滤器挑选动作名称一致的广播。

  1. private StandardReceiver standardReceiver; //声明一个标准广播的接收器实例
  2. @Override
  3. protected void onStart(){
  4. super.onStart();
  5. standardReceiver = new StandardReceiver(); //创建一个标准广播的接收器
  6. //创建一个意图过滤器,只处理STANDARD_ACTION的广播
  7. IntentFilter filter = new IntentFilter(STANDARD_ACTION);
  8. //注册接收器之后才能正常接收广播
  9. registerReceiver(standardReceiver,filter);
  10. }
  11. @Override
  12. protected void onStop(){
  13. super.onStop();
  14. unregisterReceiver(standardReceiver); //注销接收器,注销之后就不再接收广播
  15. }

 收发有序广播

        由于广播没有指定唯一的接收者,因此可能存在多个接收器,每个接收器都拥有自己的处理逻辑。这种机制固然灵活,却不够严谨,因为不同接收器之间也许有矛盾。

        因此广播的有序接收功能要求实现下列的处理逻辑:

        (1)一个广播存在多个接收器,这些接收器需要排队接收广播,这意味着该广播是条有序广播。

        (2)先收到广播的接收器A,既可以让其他接收器继续收听广播,也可以终端广播不让其他接收器接听。

        至于如何实现有序广播的收发,则需要完成以下3个编码步骤:

1.发送广播时要注明这是个有序广播

        之前发送标准广播用到了sendBroadcast方法,可是该方法发出来的广播是无序的。只有调用sendOrderedBroadcast方法才能发送有序广播。

  1. public class BroadcastType2 extends AppCompatActivity implements View.OnClickListener{
  2. private final static String ORDER_ACTION = "com.example.helloandroid.Broadcast2";
  3. private TextView tv_ReceiveBroadcast2;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.activity_broadcast_type2);
  8. findViewById(R.id.btn_SendBroadcast2).setOnClickListener(this);
  9. }
  10. @Override
  11. public void onClick(View v){
  12. if(v.getId()==R.id.btn_SendBroadcast2){
  13. Intent intent = new Intent(ORDER_ACTION); //创建一个指定动作意图
  14. sendOrderedBroadcast(intent,null); //发送有序广播
  15. }
  16. }
  17. }

2.定义有序广播的接收器

        接收器的定义代码基本不变,也要从BroadcastReceiver继承而来,唯一的区别是有序广播的接收器允许中断广播。倘若在接收器内部调用abortBroadcast方法,就会中断有序广播,使得后面的接收器不能再接收该广播。

  1. private OrderAReceiver orderAReceiver; //声明有序广播接收器A的实例
  2. //定义一个有序广播的接收器A
  3. private class OrderAReceiver extends BroadcastReceiver {
  4. //一旦接收到有序广播A,马上触发接收器的onReceive方法
  5. @Override
  6. public void onReceive(Context context,Intent intent){
  7. if (intent != null && intent.getAction().equals(ORDER_ACTION)){
  8. String desc = String.format("%s%s 接收器A收到一个有序广播\n",
  9. tv_ReceiveBroadcast2.getText().toString(), DateUtil.getNowTime());
  10. tv_ReceiveBroadcast2.setText(desc);
  11. if (ck_abort.isChecked()){
  12. abortBroadcast(); //中断广播,此时后面的接收器无法收到该广播
  13. }
  14. }
  15. }
  16. }
  17. private OrderBReceiver orderBReceiver; //声明有序广播接收器B的实例
  18. //定义一个有序广播的接收器B
  19. private class OrderBReceiver extends BroadcastReceiver {
  20. //一旦接收到有序广播B,马上触发接收器的onReceive方法
  21. @Override
  22. public void onReceive(Context context,Intent intent){
  23. if (intent != null && intent.getAction().equals(ORDER_ACTION)){
  24. String desc = String.format("%s%s 接收器A收到一个有序广播\n",
  25. tv_ReceiveBroadcast2.getText().toString(), DateUtil.getNowTime());
  26. tv_ReceiveBroadcast2.setText(desc);
  27. if (ck_abort.isChecked()){
  28. abortBroadcast(); //中断广播,此时后面的接收器无法收到该广播
  29. }
  30. }
  31. }
  32. }

3.注册有序广播的多个接收器

        接收器的注册操作同样调用registerReceiver方法,为了给接收器排队,还需调用意图过滤器的setPriority方法设置优先级,优先级越大的接收器,越先收到有序广播。如果不设置优先级,或者两个接收器的优先级相等,那么越早注册的接收器,会越先收到有序广播。

  1. @Override
  2. protected void onStart() {
  3. super.onStart();
  4. // 多个接收器处理有序广播的顺序规则为:
  5. // 1、优先级越大的接收器,越早收到有序广播;
  6. // 2、优先级相同的时候,越早注册的接收器越早收到有序广播
  7. orderAReceiver = new OrderAReceiver(); // 创建一个有序广播的接收器A
  8. // 创建一个意图过滤器A,只处理ORDER_ACTION的广播
  9. IntentFilter filterA = new IntentFilter(ORDER_ACTION);
  10. filterA.setPriority(8); // 设置过滤器A的优先级,数值越大优先级越高
  11. registerReceiver(orderAReceiver, filterA); // 注册接收器A,注册之后才能正常接收广播
  12. orderBReceiver = new OrderBReceiver(); // 创建一个有序广播的接收器B
  13. // 创建一个意图过滤器A,只处理ORDER_ACTION的广播
  14. IntentFilter filterB = new IntentFilter(ORDER_ACTION);
  15. filterB.setPriority(10); // 设置过滤器B的优先级,数值越大优先级越高
  16. registerReceiver(orderBReceiver, filterB); // 注册接收器B,注册之后才能正常接收广播
  17. }
  18. @Override
  19. protected void onStop() {
  20. super.onStop();
  21. unregisterReceiver(orderAReceiver); // 注销接收器A,注销之后就不再接收广播
  22. unregisterReceiver(orderBReceiver); // 注销接收器B,注销之后就不再接收广播
  23. }

收发静态广播 

        广播接收器也能在AndroidManifest.xml中注册,并且注册的节点名为receiver,一旦接收器在AndroidManifest.xml中注册,就无需在代码中注册。

        在AndroidManifest.xml中注册接收器,该方式被称作静态注册;在代码中注册接收器,该方式被称作为动态注册。之所以罕见静态注册,是因为静态注册容易导致安全问题,故而Android 8.0之后废弃了大多数静态注册。话虽如此,Android倒也没有彻底禁止静态注册,只要满足特定的编码条件,那么依然能够通过静态方式注册接收器。具体注册步骤说明如下。

        1.点击当前模块的默认包,依次选择右键菜单的New→Package,创建名为receiver的新包,用于存放静态注册的接收器代码。

        2.右击刚创建的receiver包,依次选择右键菜单的New→Other→Broadcast Receiver,弹出如下广播组件的创建对话框。

3.在组件对话框的Class Name一栏填写接收器的类名,比如ShockReceiver,再单机对话框右下角的Finish按钮。之后Android Studio自动再receiver包内创建代码文件ShockReceiver.java,且接收器默认代码如下: 

  1. public class ShockReceiver extends BroadcastReceiver {
  2. @Override
  3. public void onReceive(Context context, Intent intent) {
  4. // TODO: This method is called when the BroadcastReceiver is receiving
  5. // an Intent broadcast.
  6. throw new UnsupportedOperationException("Not yet implemented");
  7. }
  8. }

        同时AndroidManifest.xml自动添加接收器的节点配置,默认的receiver配置如下:

  1. <receiver
  2. android:name=".receiver.ShockReceiver"
  3. android:enabled="true"
  4. android:exported="true"></receiver>

        因为ShockReceiver未依附于任何活动,自然无法直接操作界面控制,所以只能观察程序日志,或者干脆让手机摇晃起来。实现手机震动,要调用getSystemService方法,先从系统服务VIBRATOR_SERVICE获取震动管理器Vibrator,再调用震动管理器的vibrate方法震动手机。

  1. public class ShockReceiver extends BroadcastReceiver {
  2. private static final String TAG = "ShockReceiver";
  3. // 静态注册时候的action、发送广播时的action、接收广播时的action,三者需要保持一致
  4. public static final String SHOCK_ACTION = "com.example.helloandroid.shock";
  5. @Override
  6. public void onReceive(Context context, Intent intent) {
  7. Log.d(TAG, "onReceive");
  8. if (intent.getAction().equals(ShockReceiver.SHOCK_ACTION)){
  9. // 从系统服务中获取震动管理器
  10. Vibrator vb = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
  11. vb.vibrate(500); // 命令震动器吱吱个若干秒,这里的500表示500毫秒
  12. }
  13. }
  14. }

        由于震动手机需要申请对应的权限,因此打开AndroidManifest.xml添加以下的权限申请配置

  1. <!-- 震动 -->
  2. <uses-permission android:name="android.permission.VIBRATE" />

        此外,接收器代码定义了一个动作名称,其值为"com.example.helloandroid.shock",表示onReceive方法只处理过滤该动作之后的广播从而提高接收效率。除了在代码中过滤之外,还能修改AndroidManifest.xml,在receiver节点内部增加intent-filter标签加以过滤。

  1. <receiver
  2. android:name=".receiver.ShockReceiver"
  3. android:enabled="true"
  4. android:exported="true" >
  5. <intent-filter>
  6. <action android:name="com.example.helloandroid.shock"/>
  7. </intent-filter>
  8. </receiver>

        由于Android 8.0之后删除了大部分静态注册,防止App退出后仍在收听广播,因此为了让应用能够继续接收静态广播,需要给静态广播指定包名,也就是调用意图对象的setComponent方法设置组件路径.

  1. public class BroadcastType3 extends AppCompatActivity implements View.OnClickListener {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_broadcast_type3);
  6. findViewById(R.id.btn_send_shock).setOnClickListener(this);
  7. }
  8. @Override
  9. public void onClick(View v) {
  10. if (v.getId() == R.id.btn_send_shock) {
  11. // Android8.0之后删除了大部分静态注册,防止退出App后仍在接收广播,
  12. // 为了让应用能够继续接收静态广播,需要给静态注册的广播指定包名。
  13. String receiverPath = "com.example.chapter04.receiver.ShockReceiver";
  14. Intent intent = new Intent(ShockReceiver.SHOCK_ACTION); // 创建一个指定动作的意图
  15. // 发送静态广播之时,需要通过setComponent方法指定接收器的完整路径
  16. ComponentName componentName = new ComponentName(this, receiverPath);
  17. intent.setComponent(componentName); // 设置意图的组件信息
  18. sendBroadcast(intent); // 发送静态广播
  19. Toast.makeText(this, "已发送震动广播", Toast.LENGTH_SHORT).show();
  20. }
  21. }
注        意
经过整改的静态注册只适用于接收App自身的广播,不能接收系统广播,也不能接收其他的广播。

 定时管理器 AlarmManager

        Android提供了专门的时间管理器AlarmManager,它利用系统闹钟定时发送广播,能够让App实现定时功能。由于闹钟与震动器同属系统服务,且闹钟的服务名称为ALARM_SERVICE,因此依然调用getSystemService方法获取闹钟管理器的实例。

  1. //从系统服务中获取闹钟管理器
  2. AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);

        得到闹钟实例后,即可调用它的各种方法设置闹钟规则了,AlarmManager的常见方法说明如下:

  • set:设置一次性定时器。第一个参数为定时器类型,通常填alarmManager.RTC_WAKEUP;第二个参数为期望的执行时刻(单位为毫秒);第三个参数为待执行的延迟意图(PendingIntent类型)。
  • setAndAllowWhileIdle:设置一次性定时器,参数说明同set方法,不同之处在于:即使设备处于空闲状态,也会保证执行定时器。因为从Android 6.0开始,set方法在暗屏时不保证发送广播,必须调用setAndAllowWhileIdle方法才能保证发送广播。
  • setRepeating:设置重复定时器。第一个参数为定时器类型;第二个参数为首次执行时间(单位为毫秒);第三个参数为下次执行的时间间隔(单位为毫秒);第四个参数为待执行的延迟意图(PendingIntent类型)。然而setRepeating方法不保证按时发送广播,只能通过setAndAllowWhileIdle方法间接实现重复定时功能。
  • cancel:取消指定延迟意图的定时器。

        以上的方法说明出现了新名词——延迟意图,它是PendingIntent类型,顾名思义,延迟意图不是马上执行的意图,而是延迟若干时间才执行的意图。意图与延迟意图的差异主要有下列3点:

  1. PendingIntent代表延迟的意图,它指向的组件不会马上被激活;而Intent代表实时的意图,一旦被启动,它指向的组件就会马上被激活。
  2. PendingIntent是一类消息的组合,不但包含目标的Intent对象,还包含请求代码、请求方式等信息。
  3. PendingIntent对象在创建之时便已知晓要用于活动还是广播,例如调用getActivity方法得到的是活动跳转的延迟意图,调用getBroadcast方法得到的是广播发送的延迟意图。

        就闹钟广播的收发过程而言,需要实现3个编码步骤:定义定时器的广播接收器、开关定时器的广播接收器、设置定时器的播报规则,分别叙述如下。

1.定义定时器的广播接收器

        闹钟广播的接收器采用动态注册方式,它的实现途径与标准广播类似,都要从BroadcastReceiver派生的接收器,并重写onReceiver方法。

  1. //声明一个闹钟广播事件的标识串
  2. private String ALARM_ACTION = "com.example.helloandroid.alarm";
  3. private String mDesc = ""; //闹钟时间到达的描述
  4. //定义一个闹钟广播的接收器
  5. public class AlarmReceiver extends BroadcastReceiver {
  6. //一旦接收到闹钟时间到达的广播,马上触发接收器的onReceive方法
  7. @Override
  8. public void onReceive(Context context, Intent intent) {
  9. if (intent != null) {
  10. mDesc = String.format("%s\n%s 闹钟时间到达", mDesc,DateUtil.getNowTime());
  11. tv_alarm.setText(mDesc);
  12. //从系统服务中获取震动管理器
  13. Vibrator vb = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
  14. vb.vibrate(500); //命令震动器震动若干秒
  15. }
  16. }
  17. }

2.开关定时器的广播接收器

        定时接收器的开关流程参照标准广播,可以在活动页面的onStart方法中注册接收器,在活动页面的onStop方法中注销接收器。

  1. private AlarmReceiver alarmReceiver; //声明一个闹钟的广播接收器
  2. @Override
  3. public void onStart() {
  4. super.onStart();
  5. alarmReceiver = new AlarmReceiver(); //创建一个闹钟的广播接收器
  6. //创建一个意图过滤器,只处理指定事件来源的广播
  7. IntentFilter filter = new IntentFilter(ALARM_ACTION);
  8. //注册接收器,注册之后才能正常接收广播
  9. registerReceiver(alarmReceiver,filter);
  10. }
  11. @Override
  12. public void onStop() {
  13. super.onStop();
  14. unregisterReceiver(alarmReceiver); //注销接收器,注销之后就不再接收广播
  15. }

3.设置定时器的播报规则

        首先从系统服务中获取闹钟管理器,然后调用管理器的set***方法,把事先创建的延迟意图填到播报规则当中。

  1. // 发送闹钟广播
  2. private void sendAlarm() {
  3. Intent intent = new Intent(ALARM_ACTION); // 创建一个广播事件的意图
  4. // 创建一个用于广播的延迟意图
  5. PendingIntent pIntent = PendingIntent.getBroadcast(this, 0,
  6. intent, PendingIntent.FLAG_UPDATE_CURRENT);
  7. // 从系统服务中获取闹钟管理器
  8. AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
  9. long delayTime = System.currentTimeMillis() + mDelay*1000; // 给当前时间加上若干秒
  10. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  11. // 允许在空闲时发送广播,Android6.0之后新增的方法
  12. alarmMgr.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, delayTime, pIntent);
  13. } else {
  14. // 设置一次性闹钟,延迟若干秒后,携带延迟意图发送闹钟广播(但Android6.0之后,set方法在暗屏时不保证发送广播,必须调用setAndAllowWhileIdle方法)
  15. alarmMgr.set(AlarmManager.RTC_WAKEUP, delayTime, pIntent);
  16. }
  17. }

        ps. 因内置模拟器在模拟此功能时会出现闪退现象,顾选用外部模拟器进行模拟,我选用的是夜神模拟器

 

        至于闹钟重复播报问题,因为setRepeating方法不再可靠,所以要修改闹钟的收听逻辑,在onReceive末尾补充调用sendAlarm方法,确保每次收到广播之后立即准备下一个广播。

  1. // 定义一个闹钟广播的接收器
  2. public class AlarmReceiver extends BroadcastReceiver {
  3. // 一旦接收到闹钟时间到达的广播,马上触发接收器的onReceive方法
  4. @Override
  5. public void onReceive(Context context, Intent intent) {
  6. if (intent != null) {
  7. mDesc = String.format("%s\n%s 闹钟时间到达", mDesc, DateUtil.getNowTime());
  8. tv_alarm.setText(mDesc);
  9. // 从系统服务中获取震动管理器
  10. Vibrator vb = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
  11. vb.vibrate(500); // 命令震动器吱吱个若干秒
  12. if (ck_repeate.isChecked()) { // 需要重复闹钟广播
  13. sendAlarm(); // 发送闹钟广播
  14. }
  15. }
  16. }
  17. }

 

补充: 闹钟延迟的下拉框设置

Java:

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.activity_broadcast_type4);
  5. ck_repeate = findViewById(R.id.ck_repeate);
  6. tv_alarm = findViewById(R.id.tv_alarm);
  7. findViewById(R.id.btn_alarm).setOnClickListener(this);
  8. initDelaySpinner(); // 初始化闹钟延迟的下拉框
  9. }
  10. // 初始化闹钟延迟的下拉框
  11. private void initDelaySpinner() {
  12. ArrayAdapter<String> delayAdapter = new ArrayAdapter<String>(this,
  13. R.layout.item_select, delayDescArray);
  14. Spinner sp_delay = findViewById(R.id.sp_delay);
  15. sp_delay.setPrompt("请选择闹钟延迟");
  16. sp_delay.setAdapter(delayAdapter);
  17. sp_delay.setOnItemSelectedListener(new DelaySelectedListener());
  18. sp_delay.setSelection(0);
  19. }
  20. private int[] delayArray = {5, 10, 15, 20, 25, 30};
  21. private String[] delayDescArray = {"5秒", "10秒", "15秒", "20秒", "25秒", "30秒"};
  22. class DelaySelectedListener implements OnItemSelectedListener {
  23. public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
  24. mDelay = delayArray[arg2];
  25. }
  26. public void onNothingSelected(AdapterView<?> arg0) {}
  27. }

xml:

  1. <Spinner
  2. android:id="@+id/sp_delay"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:layout_toRightOf="@+id/tv_delay"
  6. android:gravity="left|center"
  7. android:spinnerMode="dialog"/>

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/454524
推荐阅读
相关标签
  

闽ICP备14008679号