你的位置:首页 > 操作系统

[操作系统]安卓四大组件之广播


      广播是一种广泛运用的在应用程序之间传输信息的机制,Android 为了将系统运行时的各种“事件”通知给其他应用,因此内置了多种广播。广播机制最大的特点就是发送方并不关心接收方是否接到数据,也不关心接收方是如何处理数据的。Android 中的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收到自己所关心的广播内容,这些广播可能是来自于系统的,也可能是来自于其他应用程序的,前者是系统广播,后者是自定义广播。广播在具体的项目中应用场景并不多,但一旦使用会使得程序变得精简很多,因此本片文章就简单介绍一下安卓系统的广播机制。

      首先,简单的介绍一下安卓的广播机制。BroadCastReceiver是对发送出来的Broadcast 进行过滤接受并响应的一类组件,是Android四大组件之一,主要用于接收系统或者app发送的广播事件。在我们的项目中经常使用广播接收者接收系统通知,比如开机启动、sd挂载、低电量、外播电话、锁屏等。 如果我们做的是播放器,那么监听到用户锁屏后我们应该将我们的播放之暂停等。android的四大组件本质上就是为了实现移动或者说嵌入式设备上的MVC架构,它们之间有时候是一种相互依存的关系, 有时候又是一种补充关系,引入广播机制可以方便几大组件的信息和数据交互。广播有利于程序间互通消息,例如在自己的应用程序内监听系统来电。 

       BroadCastReceiver的一般编写步骤:

        1. 写一个类继承BroadCastReceiver;

        2. 重写oncreat()方法;

        3. 注册广播。动态注册和静态注册,前者在java代码中实现,后这在清单文件中编写。动态注册需要写BroadCastReceiver的实现类和过滤器,静态注册除了写过滤器外还要在receiver标签的name属性上添加包名和类名。

       按照广播的属性来分,广播分两种:有序广播和无序广播。

      无序广播:又叫普通广播,完全异步,不会被某个广播接收者终止,逻辑上可以被任何广播接收者接收到,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。优点是效率较高。缺点是一个接收者不能将处理结果传递给下一个接收者,并无法终止广播intent的传播。Context.sendBroadcast() 发送的是普通广播,所有订阅者都有机会获得并进行处理。

      有序广播:按照被接收者的优先级顺序,在被接收者中依次传播。比如有三个广播接收者A,B,C,优先级是A > B > C。那这个消息先传给A,再传给B,最后传给C。,因此通常需要在AndroidManifest.

      对于有序广播有一个小细节,那就是优先级高的广播可以终结一个广播。终止一个有序广播:abortBroadcast()。终止有序广播只需要一句代码,该代码是BroadCastReceiver 类中的方法,因此这里可以直接使用。这里需要注意的是如果abortBroadCast 是在一个无序广播中执行的,那么就会报如下异常:

          java.lang.RuntimeException:

          BroadcastReceiver trying to return result during a non-ordered broadcast

      在低版本的手机上比如Android2.3 上是不会报这样的异常的,安卓工程师认为终止无序广播是不合法的操作,因此在Android2.3之后的版本,终止无序广播都是非法操作。为了防止我们终止一个无序广播导致报异常,我们可以先判断接收到的广播类型。优化后的代码如下:

       if (isOrderedBroadcast()) {

              abortBroadcast();

       }

       isOrderedBroadcast 方法是BroadCastReceiver 类提供的,用于判断当前的广播类型。返回true 为有序广播,返回false 为无序广播。

       之后,再来讲一下广播的注册机制。广播注册方式有两种,动态注册和静态注册。在清单文件中注册广播接收者称为静态注册,在代码中注册称为动态注册。静态注册的广播接收者只要app在系统中运行则一直可以接收到广播消息,动态注册的广播接收者当注册的Activity或者Service销毁了那么就接收不到广播了。

       静态注册:在清单文件中进行如下配置

<receiver android:name="包名+类名" >  <intent-filter>    <action android:name="android.intent.action.CALL" > </action>  </intent-filter> </receiver>

 

      动态注册:在代码中进行如下注册

//广播发送者:Intent intent = new Intent();intent.setAction(Uri);intent.putExtra("packname", packname);sendBroadcast(intent);//自定义广播接受中:receiver = new BroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Uri);context.registerReceiver(receiver, intentFilter);

        

        按照广播的编写方式来分,又可以分为系统广播和自定义广播两种。

       系统广播的应用较为普遍,比如文章开头所讲的:系统电量的改变、屏幕的锁屏、网络状态的改变、接收到新的短信、拨打电话事件、sdcard 的挂载和移除、应用的安装和卸载等等。比如我们开发的在线播放视频类的APP,那么我们就有必要监听网络转态改变的事件广播,如果用户的网络状态从wifi 改变为了4G 上网,那么应该提示用户是否使用4G 网络继续播放视频,如果不提示用户,那么就可能导致用户流量被大量使用,一会儿功夫,用户可能就要停机了。这些都是通过系统广播完成。Android给许多系统服务广播Intent。你可以使用这些基于系统事件的消息来给自己的工程增添一些功能,这些事件如时区变更、数据连接状态、SMS消息或电话呼叫。系统广播的编写也较为简单,通过查看底层源码获取相关的action节点及其他参数,然后把这些写在清单文件中或动态写在java代码中,就可以在BroadCastReceiver的实现类中接受广播了。文章最后给出一个屏幕点亮和熄灭的案例,再次就不在赘述。       

       给出几种常用的系统广播监听事件中清单文件的写法。当然若是动态注册,只需要在java代码中添加过滤器即可,节点参数类似。

<--!监听SD卡状态的receiver节点:--><receiver android:name="包名+类名" > <intent-filter> <action android:name="android.intent.action.MEDIA_UNMOUNTED"/> <data android:scheme="file"></data> </intent-filter> </receiver><--!开机监听:--><receiver android:name="包名+类名"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" > </action></intent-filter> </receiver><--!电话监听:--><receiver android:name="包名+类名">   <intent-filter> <action android:name="android.intent.action.NEW_OUTGOING_CALL" />   </intent-filter> </receiver><--!程序安装和卸载的监听:--><receiver android:name="包名+类名">   <intent-filter >    <action android:name="android.intent.action.PACKAGE_ADDED"/>    <action android:name="android.intent.action.PACKAGE_REMOVED"/>     <data android:scheme="package"></data>   </intent-filter> </receiver><--!短信监听:--><receiver android:name="包名+类名" >    <intent-filter android:priority="1000" >      <action android:name="android.provider.Telephony.SMS_RECEIVED" />     </intent-filter> </receiver>

 

        上面列出来的是几种较为常见的系统广播的参数写法,当然还有一些并未写出,比如,电量低、日期更改等等,因此可以在需要编写时查阅相关的底层源码获取。

        再来讲一下自定义广播。

        自定义广播的应用场景通常是,两个程序间发送数据。例如,用看门狗程序实现程序锁,当密码登录界面上密码输入正确时,立即通知看门狗程序跳过轮训,这样就可以进入程序。自定义的广播同样可以分为有序关播和无序广播两种,下面分别给出自定义的无序广播和自定义的有序广播两个案例。

       自定义无序广播,定义两个程序,利用广播机制发送数据。

       广播发送者:

import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;public class MainActivity extends Activity {  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);  }  /**   * 发送关播   * @param view   */  public void click(View view){    Intent intent = new Intent();    intent.setAction("com.example.sendbroadcast.mysend"); //可以自定义action的值    intent.putExtra("data", "用广播传递的数据");    sendBroadcast(intent);  }}

         广播接受者:

import android.os.Bundle;import android.app.Activity;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.view.Menu;public class MainActivity extends Activity {  private myReceiver receiver;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);//     两种注册的方式,在java代码中和清单文件中    receiver = new myReceiver();    IntentFilter filter = new IntentFilter();    filter.addAction("com.example.sendbroadcast.mysend");    registerReceiver(receiver, filter);  }  class myReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {      System.out          .println("收到广播发送者发来的数据: " + intent.getStringExtra("data"));    }  }  /**   * 程序关闭时销毁广播   */  @Override  protected void onDestroy() {    super.onDestroy();    unregisterReceiver(receiver);    receiver = null;  }}

         上述程序还可以在清单文件中注册。

    <receiver android:name="com.example.receivebroadcast.MyBroadcastReceiver" >      <intent-filter>        <action android:name="com.example.sendbroadcast.mysend" />      </intent-filter>    </receiver>

 

         自定义有序广播。本文给出的案例是一个消息传递机制,有消息发布者发布一个消息,一次传递给四个人,优先级高的接受者修改消息。

         消息发布者:

import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;public class MainActivity extends Activity {  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);  }    public void click(View view){    Intent intent = new Intent();    intent.setAction("com.example.sendordermessage");    //new MyReceiver()是一个监视程序,监听最后广播的数据    sendOrderedBroadcast(intent, null, new MyReceiver(), null, 1, "我是消息发布者, 我发布的消息是: 街上有一只狼", null);  }}

        另外在广播发布者中,写一个监视程序,查看最后一个广播接受者接受到的数据。

import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;public class MyReceiver extends BroadcastReceiver {  @Override  public void onReceive(Context context, Intent intent) {     System.out.println("我是内线程序,我收到的消息是: " + getResultData());  }}

 

        第一个广播接受者:

import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;public class FirstReceiver extends BroadcastReceiver {  @Override  public void onReceive(Context context, Intent intent) {     System.out.println("我是第一个消息接受者,我听说" + getResultData());     setResultData("街上有5只狼");  }}

 

      第二个广播接受者:

import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;public class SecondReceiver extends BroadcastReceiver {  @Override  public void onReceive(Context context, Intent intent) {     System.out.println("我是第二个消息接受者,我听说" + getResultData());     setResultData("街上有10只猴");  }}

       第三个广播接受者:

import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;public class ThirldReceiver extends BroadcastReceiver {  @Override  public void onReceive(Context context, Intent intent) {     System.out.println("我是第三个消息接受者,我听说" + getResultData());     setResultData("街上有20只狼");  }}

       第四个广播接受者:

import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;public class FourthReceiver extends BroadcastReceiver {  @Override  public void onReceive(Context context, Intent intent) {     System.out.println("我是第四个消息接受者,我听说" + getResultData());  }}

       广播接受者的清单文件的配置

    <receiver android:name="com.example.receiverorder.FirstReceiver" >      <intent-filter android:priority="1000" >        <action android:name="com.example.sendordermessage" />      </intent-filter>    </receiver>    <receiver android:name="com.example.receiverorder.SecondReceiver" >      <intent-filter android:priority="500" >        <action android:name="com.example.sendordermessage" />      </intent-filter>    </receiver>    <receiver android:name="com.example.receiverorder.ThirldReceiver" >      <intent-filter android:priority="0" >        <action android:name="com.example.sendordermessage" />      </intent-filter>    </receiver>    <receiver android:name="com.example.receiverorder.FourthReceiver" >      <intent-filter android:priority="-1000" >        <action android:name="com.example.sendordermessage" />      </intent-filter>    </receiver>

 

      工程的目录结构:

                  

          成果展示:

               

        最后补充一个小内容。在Android 中一些操作比较频繁的事件,比如锁屏解屏和电量的变化,也会发送特定的广播。但是此类广播的注册是无法注册在AndroidManifest.BroadcastReceiver,在该类中接受关播信息。

        

import android.app.Activity;import android.content.BroadcastReceiver;import android.content.ContentValues;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.os.Bundle;public class MainActivity extends Activity {  private myReceiver myreceiver;  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    IntentFilter filter = new IntentFilter();    filter.addAction(Intent.ACTION_SCREEN_OFF);    filter.addAction(Intent.ACTION_SCREEN_ON);    myreceiver = new myReceiver();    registerReceiver(myreceiver, filter);  }  /**   * 广播实现类   *   */  class myReceiver extends BroadcastReceiver{    @Override    public void onReceive(Context context, Intent intent) {      System.out.println(intent.getAction().toString());    }  }    /**   * 反注册广播   *   */  @Override  protected void onDestroy() {    super.onDestroy();    unregisterReceiver(myreceiver);    myreceiver = null;  }}

        自此,广播的内容全部讲解完毕。