• 安卓四大组件之广播


          广播是一种广泛运用的在应用程序之间传输信息的机制,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.xml 中进行注册,优先级别声明在intent-filter 元素的android:priority 属性中,数越大优先级别越高,取值范围:-1000 到1000,优先级别也可以调用IntentFilter 对象的setPriority()进行设置。有序广播的接收者可以终止广播的传播,广播的传播一旦终止,后面的接收者就无法接收到广播,有序广播的接收者可以将数据传递给下一个接收者,如:A 得到广播后,可以往它的结果对象中存入数据,当广播传给B 时,B 可以从A 的结果对象中得到A 存入的数据。Context.sendOrderedBroadcast() 发送的是有序广播。Bundlebundle = getResultExtras(true))可以获取上一个接收者存入在结果对象中的数据。

          对于有序广播有一个小细节,那就是优先级高的广播可以终结一个广播。终止一个有序广播: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.xml 中,只能在代码中进行注册。也就是说对于锁屏解屏和电量变化的监听只能通过动态注册。亮屏和息屏的操作经常用于省电处理中,当屏幕熄灭时可以关闭掉一些耗时的服务,当屏幕点亮时再启动服务。这样既保证了用户的使用体验,同时减少了手机的耗电量,对于产品优化大有裨益。下面给出屏幕广播监视案例,写一个内部类继承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;
        }
    }

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

     

     

     

     

     

     

     

  • 相关阅读:
    oracle 自动备份
    oracle 常用操作语句
    数据库创建及使用注意事项
    oracle 导入 导出 备份
    http://blog.sina.com.cn/s/blog_5fc8b3810100iw9n.html
    利用普通工具编译的第一个Servlet
    对java:comp/env的研究(转)
    MyEclipse配置tomcat、jdk和发布第一个web项目
    构建 SSH 框架(转)
    Java Project和Web Project
  • 原文地址:https://www.cnblogs.com/huangjie123/p/6052104.html
Copyright © 2020-2023  润新知