Android Day07四大组件之广播接收者BroadcastReceiver

简介:




1.广播和广播接收者

    Android中的广播和广播接收者相当于现实生活中的电台和听收音机的人,

   Android系统内部相当于已经定义好了电台, 就是内部定义好了一些事件(外拨电话,短信到来 

 电池电量低 sd卡状态 卸载安装  开机启动等等),我们开发者只需要注册这个事件就ok了(注册事

 件就是创建广播接收者之后在清单里配置意图过滤器的action属性,相当于听收音机的人调频一样)

   更确切的说,广播接收者就是Android系统的全局监听者,可以监听系统的很多事件,只要广播

 接收者注册了这些事件,就会被这些事件触发。

   广播接收者被触发,底层原理应该是广播事件会利用隐式意图启动符合条件的广播接收者. 

 

2.注册广播接收者

   注册广播接收者有2种方式,而且一个广播接收者配置多个过滤动作Action。

   第1种:在清单里注册

    在配置文件中注册的接收者的特点是即使应用程序已被关闭,该接收者依然可接受它感兴趣的广播

                上面红色特性,已经验证过,确实如此!

    一般大多数的广播接收者都是在清单文件中注册,只有少量的广播接收者是在代码里注册


    当然可以在清单里注册的广播接收者,也一定能够在代码里注册。  

    wKioL1W6rLrBDM5LAAJEDutJOFw011.jpg

   第2种:在Activity里注册

     在Activity中绑定接收者必须依附该应用程序存在,或者一个BroadcastReceiver用于更新UI,就没有必要在

                程序关闭时接收者还运行,故无需在AndroidManifest.xml中注册而可以放在Activity中注册。 


      操作特别频繁的广播事件  比如 锁屏和解锁这种广播接收者在清单文件里面注册是无效的

    必需在代码里注册,否则会报下面这个异常。

      MainActivity has leaked IntentReceiver

      ScreenReceiver@b65bb500 that was originally registered here. Are you missing a

       call to unregisterReceiver()? 


      代码里注册广播接收者

      首先,定义一个类继承BroadcastReceiver:

        public class ScreenReceiver extends BroadcastReceiver

      下一步,在一个Activity中注册上面定义的广播接收者  

1
2
3
4
5
6
7
         ScreenReceiver screenReceiver =  new  ScreenReceiver();
         //创建意图对象过滤器
         IntentFilter intentFilter =  new  IntentFilter();
         intentFilter.addAction( "android.intent.action.SCREEN_OFF" );
         intentFilter.addAction( "android.intent.action.SCREEN_ON" );
         //注册锁屏和解锁广播
         registerReceiver(screenReceiver, intentFilter);

 
3.广播接收者开发步骤

   第1步:定义一个类继承广播接收者,并在清单或代码里注册,包括receiver的name属性、

       过滤器的Action属性。

    第2步:复写定义的广播接收者类的onReceive方法,定义广播接收者被触发后想实现的逻辑。

       一般如果广播接收者注册了多个事件,还得先判断一下事件的类型。

1
2
3
4
             public  void  onReceive(Context context, Intent intent) {  
                 //得到事件的类型
         String action = intent.getAction();
            


案例:注意不同的案例获取数据的方式

4.案例1_IP拨号器(外拨电话)

   ■清单配置

1
2
3
4
5
6
     < receiver  android:name = "com.itheima.ipdialerListener.OutGoingReceiver"  >
       < intent-filter >
          <!-- 配置action new outgoing call -->
          < action  android:name = "android.intent.action.NEW_OUTGOING_CALL" />
       </ intent-filter >
     </ receiver >

   ■权限 

1
      < uses-permission  android:name = "android.permission.PROCESS_OUTGOING_CALLS" />

   ■广播接收者的接收后的逻辑  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
     public  class  OutGoingReceiver  extends  BroadcastReceiver {
 
     @Override
     public  void  onReceive(Context context, Intent intent) {
         //获取广播事件的数据-电话号码
         String phoneNumber = getResultData();    //直接调用BroadcastReceiver的方法
         System.out.println( "phoneNumber:"  + phoneNumber);
         
         //假如电话号码开头是0,就是长途。
         if (phoneNumber.startsWith( "0" ))
         {
             phoneNumber =  "95128"  + phoneNumber;
         }
         
         //改变广播当前的数据-用修改过后电话号码替换。
         setResultData(phoneNumber);    //注意这个方法只能与有序广播联用 
     }
     }

  将应用程序安装好,拨打一个手机号以0开始的电话号码0123,点击拨打按钮,效果如下所示:

  wKiom1XA2BHASpx-AADrCjxS6ls348.jpg



5.案例2_sd卡状态监听

      

   ■清单配置

     注意过滤器里多了一个<data android:scheme="file"/>

1
2
3
4
5
6
7
      < receiver  android:name = "com.itheima.sdcard.SDCardReceiver"  >
             < intent-filter >
                 < action  android:name = "android.intent.action.MEDIA_MOUNTED" />
                 < action  android:name = "android.intent.action.MEDIA_UNMOUNTED" />
                 < data  android:scheme = "file" />
             </ intent-filter >
         </ receiver >

   ■权限 

1
     

   ■广播接收者的接收后的逻辑  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
     public  class  SDCardReceiver  extends  BroadcastReceiver {
 
             @Override
             public  void  onReceive(Context context, Intent intent) {
                     //因为清单里有2个action,所以要获取action的类型。
                 String action = intent.getAction();
                 if (Intent.ACTION_MEDIA_MOUNTED.equals(action))
                 {
                     System.out.println( "sd卡挂载了" );
                 } else  if (Intent.ACTION_MEDIA_UNMOUNTED.equals(action)){
                     System.out.println( "sd卡卸载了" );
                 } else  {
                     System.out.println( "sd监听出错了" );
                 }
             }
         }

   进入2.3这些低版本的模拟器,settings-storage里面可以模拟sd的挂载与卸载。

    wKioL1XA3XjB7DcVAAB1Pmxdrmk528.jpg



6.案例3_短信监听器

    短信监听的底层原理,应该是短信数据库内容提供者者安插了内容观察者,一旦手机接收到短

  信,势必短信数据库会发生变化,内容观察者分析到了就会发送一条短信接收的广播,并且把短

  信相关的信息封装在了意图对象里。  

   ■清单配置

      高版本的ADT提示里没有action对应的选项,应从低版本ADT拷贝action值到清单中。

1
2
3
4
5
     < receiver  android:name = "com.itheima.msglistener.MsgListener"  >
             < intent-filter >
                 < action  android:name = "android.provider.Telephony.SMS_RECEIVED" />
             </ intent-filter >
         </ receiver >

   ■权限 

1
      < uses-permission  android:name = "android.permission.RECEIVE_SMS" />

   ■广播接收者的接收后的逻辑  

     注意SmsMessage对象所在的包   

1
2
3
4
5
6
7
8
9
10
11
12
13
     //获取短信数据,可能有多条短信数据吧。
     //由此可见,短信的数据在intent里的封装的格式是Bundle,也就是map集合。
         Object[] object = (Object[]) intent.getExtras().get( "pdus" );
         
     //创建SmsMessage对象来分离短信的各项内容 ,注意SmsMessage所在的包。
         for  (Object obj : object) {
             //创建SmsMessage实例,解析短信信息:发送者、短信内容
             SmsMessage smsMessage = SmsMessage.createFromPdu(( byte []) obj);
             String messageBody = smsMessage.getMessageBody();
             String originatingAddress = smsMessage.getOriginatingAddress();
             System.out.println( "短信发送者:"  + originatingAddress);
             System.out.println( "短信内容:"  + messageBody);
         }

   

    关于短信监听,android2.3版本时没有考虑到安全问题,4.0之后,考虑到安全问题,广播接

  收者需要有一个启动界面才能生效(但不一定要求界面就要启动,也就是说只要有个activity就行了

  )。

    对于用户来说,在2.3这些低版本设备上,对于没有界面的短信监听应用,如果一旦有短信发送过

  来,应用就会被激活,在setting中的forcestop按钮也才会变得可选,可以在setting中点击这个按

  钮就能关闭应用

    wKioL1XCG7SQ_Fs_AAC328auDug639.jpg

    对于流氓程序员来说,不但可以做个没有启动界面和图标的应用,更可以结合卸载监听事件,不让

  安装的流氓应用卸载,还可以加个密码确认之后才能输入,要多流氓就有多流氓。

 

7.案例4_应用安装与卸载的监听

   

   ■清单配置

      注意过滤器多了一个<data android:scheme="package" />

1
2
3
4
5
6
7
      < receiver  android:name = "com.itheima.packageaddandremove.PackageReceiver"  >
             < intent-filter >
                 < action  android:name = "android.intent.action.PACKAGE_ADDED"  />
                 < action  android:name = "android.intent.action.PACKAGE_REMOVED"  />
                 < data  android:scheme = "package"  />
             </ intent-filter >
         </ receiver >

   ■权限 

1
     

   ■广播接收者的接收后的逻辑  

1
2
3
4
5
6
7
8
9
10
11
12
13
     //得到包名
         Uri packageName  = intent.getData();
         
     //获取事件类型
         String action = intent.getAction();
         if ( "android.intent.action.PACKAGE_ADDED" .equals(action))
         {
             System.out.println(packageName +  "安装了。。。。。" );
         }
         else  if ( "android.intent.action.PACKAGE_REMOVED" .equals(action))
         {
             System.out.println(packageName +  "卸载了。。。。。" );
         }

   

8.案例5_开机启动监听

  如果想在广播接收者里启动activity,必须要为意图指定一个任务栈标记。

   ■清单配置

      wKioL1XCIhGBur5qAAD0JOa1w8g131.jpg


   ■权限 

    wKiom1XCID3j72fZAAB28IFVRfw490.jpg

   ■广播接收者的接收后的逻辑  

    wKioL1XCIpuSe-_UAAE_TeXcGWM926.jpg

  开机启动广播监听,可以与很多地方相结合。如启动activity或service,可以在service里又与

 其它的监听事件如短信、电话监听相结合,做出电话窃听器这样子之类的应用。

   另外,开机启动监听比较的费时,有时候其实是实现了,但是 Log就是不出信息,可以采用开

 启Activity的方式(记得intent要setFlags),这样效果就明显了。


####################################################################################

上面讲到几个案例,都有一个共同的特点:广播都是由系统发送的。那么可以自己定义并发送广播吗?


查看API会发现Context类有发送广播的方法,Activity和Service都继承了Context,所以这两个对象

都可以发送广播,或者只要有Context对象的对象都可以发送广播。广播就是在发送Intent,Intent用

来封装数据并且过滤广播接收者,发送广播有2种方式:(系统发送广播也是用的这2个方法)


1.发送无序广播

   ■发送广播代码:sendBroadcast

1
2
3
4
5
6
7
8
9
10
  public  void  sendUnOrder(String content)
     {
         Intent intent =  new  Intent();
         //注意设置意图对象的action,以便过滤发送给哪个广播接收者。
         intent.setAction( "com.itheima.UNORDER_SEND" );
         intent.putExtra( "content" , content);
         //无序广播的发送方法
         sendBroadcast(intent);
         
     }

   广播接收者清单配置:    

   只要定义一个广播接收者,在清单里配置它的action属性和发送代码里的action参数一样,发送广

 播的方法就会将意图Intent发送给符合条件的广播接收者的onReceive参数里,这也印证了为什么广播

 接收者的接收方法里会有一个Intent参数的原因:onReceive参数里的Intent就是发送这个广播的对象

 传递的Intent。

   

2.发送有序广播

  ■发送广播代码:sendOrderedBroadcast    

1
2
3
4
5
6
7
  public  void  sendOrder(String content)
     {
         Intent intent =  new  Intent();
         intent.setAction( "com.itheima.ORDER_SEND" );
         //注意有序广播的发送方法
         sendOrderedBroadcast(intent,  null new  FinalReceiver(),  null 1 , content,  null );
     }

  广播接收者清单配置:  

    和接收无序广播的广播接收者不同,接收有序广播的广播接收者的过滤器除了要配置action

   标签之外,过滤器本身还要设置priority属性,表示过滤器的优先级,优先级高的过滤器的广播

   接收者会优先接收到广播。

    终极接收者无须在清单配置任何信息,连receiver也不用配置。

     wKioL1XCLInC4xqKAAJtgxAbmuk336.jpg

   广播接收者接收广播代码: 

     wKiom1XCLJuC0dUGAAInhvkbZB4753.jpg  

    

3.有序广播与无序广播的区别(**********重点***********)

  ■有序广播 

    广播的数据可以被修改,广播可以被拦截,但是终极接收者仍然可以接受到。

  ■无序广播  

     广播的数据不可以被修改,广播不可以被拦截。

 



--------------------------------广播的一些异常------------------------------------

1)IllegalStateException: Can not perform this action after onSaveInstanceState

    http://www.tuicool.com/articles/yU3Yji

 http://stackoverflow.com/questions/7575921/illegalstateexception-can-not-perform-this-action-after-onsaveinstancestate-wit

  

  一是要解注册广播,二是在fragmentManager提交的时候采用

transaction.commitAllowingStateLoss();


 



      本文转自屠夫章哥  51CTO博客,原文链接:http://blog.51cto.com/4259297/1679400,如需转载请自行联系原作者





相关文章
|
29天前
|
存储 Android开发 开发者
深入理解安卓应用开发的核心组件
【10月更文挑战第8天】探索Android应用开发的精髓,本文带你了解安卓核心组件的奥秘,包括Activity、Service、BroadcastReceiver和ContentProvider。我们将通过代码示例,揭示这些组件如何协同工作,构建出功能强大且响应迅速的应用程序。无论你是初学者还是资深开发者,这篇文章都将为你提供新的视角和深度知识。
|
1月前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
65 0
|
1月前
|
XML 前端开发 Java
安卓应用开发中的自定义View组件
【10月更文挑战第5天】自定义View是安卓应用开发的一块基石,它为开发者提供了无限的可能。通过掌握其原理和实现方法,可以创造出既美观又实用的用户界面。本文将引导你了解自定义View的创建过程,包括绘制技巧、事件处理以及性能优化等关键步骤。
|
1月前
|
测试技术 数据库 Android开发
深入解析Android架构组件——Jetpack的使用与实践
本文旨在探讨谷歌推出的Android架构组件——Jetpack,在现代Android开发中的应用。Jetpack作为一系列库和工具的集合,旨在帮助开发者更轻松地编写出健壮、可维护且性能优异的应用。通过详细解析各个组件如Lifecycle、ViewModel、LiveData等,我们将了解其原理和使用场景,并结合实例展示如何在实际项目中应用这些组件,提升开发效率和应用质量。
40 6
|
2月前
|
存储 开发框架 数据可视化
深入解析Android应用开发中的四大核心组件
本文将探讨Android开发中的四大核心组件——Activity、Service、BroadcastReceiver和ContentProvider。我们将深入了解每个组件的定义、作用、使用方法及它们之间的交互方式,以帮助开发者更好地理解和应用这些组件,提升Android应用开发的能力和效率。
159 5
|
2月前
|
缓存 搜索推荐 Android开发
安卓应用开发中的自定义View组件实践
【9月更文挑战第10天】在安卓开发领域,自定义View是提升用户体验和实现界面个性化的重要手段。本文将通过一个实际案例,展示如何在安卓项目中创建和使用自定义View组件,包括设计思路、实现步骤以及可能遇到的问题和解决方案。文章不仅提供了代码示例,还深入探讨了自定义View的性能优化技巧,旨在帮助开发者更好地掌握这一技能。
|
3月前
|
存储 搜索推荐 Java
探索安卓开发中的自定义视图:打造个性化UI组件Java中的异常处理:从基础到高级
【8月更文挑战第29天】在安卓应用的海洋中,一个独特的用户界面(UI)能让应用脱颖而出。自定义视图是实现这一目标的强大工具。本文将通过一个简单的自定义计数器视图示例,展示如何从零开始创建一个具有独特风格和功能的安卓UI组件,并讨论在此过程中涉及的设计原则、性能优化和兼容性问题。准备好让你的应用与众不同了吗?让我们开始吧!
|
3月前
|
XML 搜索推荐 Android开发
安卓开发中的自定义View组件实践
【8月更文挑战第30天】探索Android世界,自定义View是提升应用界面的关键。本文以简洁的语言带你了解如何创建自定义View,从基础到高级技巧,一步步打造个性化的UI组件。
|
3月前
|
开发工具 Android开发
Android项目架构设计问题之组件A通知组件B某个事件的发生如何解决
Android项目架构设计问题之组件A通知组件B某个事件的发生如何解决
39 0
|
4月前
|
机器学习/深度学习 人工智能 算法
探索AI在医疗影像分析中的应用探索安卓开发中的自定义View组件
【7月更文挑战第31天】随着人工智能技术的飞速发展,其在医疗健康领域的应用日益广泛。本文将聚焦于AI技术在医疗影像分析中的运用,探讨其如何通过深度学习模型提高诊断的准确性和效率。我们将介绍一些关键的深度学习算法,并通过实际代码示例展示这些算法是如何应用于医学影像的处理和分析中。文章旨在为读者提供对AI在医疗领域应用的深刻理解和实用知识。
44 0
下一篇
无影云桌面