zoukankan      html  css  js  c++  java
  • Android组件系列----BroadcastReceiver广播接收器

    【声明】 

    欢迎转载,但请保留文章原始出处→_→ 

    生命壹号:http://www.cnblogs.com/smyhvae/ 

    文章来源:http://www.cnblogs.com/smyhvae/p/3960623.html

     

    【正文】

    一、广播的功能和特征

    • 广播的生命周期很短,经过调用对象-->实现onReceive-->结束,整个过程就结束了。从实现的复杂度和代码量来看,广播无疑是最迷你的Android 组件,实现往往只需几行代码。广播对象被构造出来后通常只执行BroadcastReceiver.onReceive方法,便结束了其生命周期。所以有的时候我们可以把它当做函数看也未必不可。
    • 和所有组件一样,广播对象也是在应用进程的主线程中被构造,所以广播对象的执行必须是要同步且快速的。也不推荐在里面开子线程,因为往往线程还未结束,广播对象就已经执行完毕被系统销毁。如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由 Service 来完成。
    • 每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 , 该对象即被销毁 . 当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应。

    二、接收系统广播:

    广播接收器可以自由地对自己感兴趣的广播进行注册,这样当有相应的广播发出时,广播接收器就能收到该广播,并在内部处理相应的逻辑。注册广播的方式有两种,在代码中注册和在清单文件中注册,前者称为动态注册,后者称为静态注册

    1、动态注册监听网络变化:

    新建工程文件,首先在MainActivity中定义一个内部类netWorkChangeReceiver,并重写父类的onReceive()方法,这样每当网络状态发生变化时,onReceive()方法就会得到执行,这里使用Toast提示一段文本信息,代码如下:

    class netWorkChangeReceiver extends BroadcastReceiver {
            @Override
            public void onReceive(Context context, Intent intent) {
                Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();
            }        
        }

    紧接着在onCreate方法中进行动态注册,然后在onDestroy方法中进行取消注册:

     1     private IntentFilter intentFilter;
     2     private netWorkChangeReceiver netWorkChangeReceiver;
     3     
     4     @Override
     5     protected void onCreate(Bundle savedInstanceState) {
     6         super.onCreate(savedInstanceState);
     7         setContentView(R.layout.activity_main);
     8         
     9         //动态注册:创建一个IntentFilter的实例,添加网络变化的广播(功能是对组件进行过滤,只获取需要的消息)
    10         intentFilter = new IntentFilter();
    11         intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
    12         //创建NetWorkChangeReceiver的实例,并调用registerReceiver()方法进行注册
    13         netWorkChangeReceiver = new netWorkChangeReceiver();
    14         registerReceiver(netWorkChangeReceiver, intentFilter);
    15         
    16     }
    17 
    18     //取消注册,一定要记得,不然系统会报错
    19     @Override
    20     protected void onDestroy() {
    21         super.onDestroy();
    22         unregisterReceiver(netWorkChangeReceiver);
    23     }

    上方代码解释如下:

    11行:给意图过滤器intentFilter添加一个值为android.net.conn.CONNECTIVITY_CHANGE的action。因为每当网络状态发生变化时,系统就会发出一条值为android.net.conn.CONNECTIVITY_CHANG的广播。

    注:最后要记得,动态注册的广播接收器一定要取消注册才行

    运行程序,就可以了。

    不过只是提醒网络发生变化还不够人性化,为了能够准确的告诉用户当前是有网络还是没有网络,我们还需要对上述代码进一步优化,修改netWorkChangeReceiver中的代码如下:

     1     class netWorkChangeReceiver extends BroadcastReceiver {
     2         
     3         @Override
     4         public void onReceive(Context context, Intent intent) {
     5             //通过getSystemService()方法得到connectionManager这个系统服务类,专门用于管理网络连接
     6             ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
     7             NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
     8             if(networkInfo != null && networkInfo.isAvailable()){
     9                 Toast.makeText(context, "network is available",Toast.LENGTH_SHORT).show();
    10             }else{
    11                 Toast.makeText(context, "network is unavailable",Toast.LENGTH_SHORT).show();
    12             }
    13                 
    14         }
    15     }

    上方代码解释:

    06行:在onReceive()方法中,首先通过通过getSystemService()方法得到connectionManager这个系统服务类,专门用于管理网络连接。

    07行:然后调用它的getActiveNetworkInfo()方法可以得到NetworkInfo的实例,接着调用NetworkInfo的isAvailable()方法,就可以判断当前是否有网络了,最后通过Toast提示用户。

    另外,查询系统的网络状态是需要申明权限的,打开清单文件,添加如下权限:

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

    注:访问http://developer.android.com/reference/android/Manifest.permission.html可以查看Android系统所有的可声明的权限。

    现在运行程序,就可以了。

    上方程序完整版代码如下:

     1 package com.example.m05_broadcastreceiver01;
     2 import android.app.Activity;
     3 import android.content.BroadcastReceiver;
     4 import android.content.Context;
     5 import android.content.Intent;
     6 import android.content.IntentFilter;
     7 import android.net.ConnectivityManager;
     8 import android.net.NetworkInfo;
     9 import android.os.Bundle;
    10 import android.widget.Toast;
    11 public class MainActivity extends Activity {
    12     private IntentFilter intentFilter;
    13     private netWorkChangeReceiver netWorkChangeReceiver;
    14     @Override
    15     protected void onCreate(Bundle savedInstanceState) {
    16         super.onCreate(savedInstanceState);
    17         setContentView(R.layout.activity_main);
    18         // 动态注册:创建一个IntentFilter的实例,添加网络变化的广播
    19         intentFilter = new IntentFilter();
    20         intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
    21         // 创建NetWorkChangeReceiver的实例,并调用registerReceiver()方法进行注册
    22         netWorkChangeReceiver = new netWorkChangeReceiver();
    23         registerReceiver(netWorkChangeReceiver, intentFilter);
    24     }
    25     // 取消注册,一定要记得,不然系统会报错
    26     @Override
    27     protected void onDestroy() {
    28         super.onDestroy();
    29         unregisterReceiver(netWorkChangeReceiver);
    30     }
    31     class netWorkChangeReceiver extends BroadcastReceiver {
    32         @Override
    33         public void onReceive(Context context, Intent intent) {
    34             //通过getSystemService()方法得到connectionManager这个系统服务类,专门用于管理网络连接
    35             ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    36             NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
    37             if(networkInfo != null && networkInfo.isAvailable()){
    38                 Toast.makeText(context, "network is available",Toast.LENGTH_SHORT).show();
    39             }else{
    40                 Toast.makeText(context, "network is unavailable",Toast.LENGTH_SHORT).show();
    41             }
    42                 
    43         }
    44     }
    45 }
    View Code 

    2、静态注册实现开机启动:

    动态注册的方式比较灵活,但缺点是:必须在程序启动之后才能接收到广播,因为注册的逻辑是写在onCreate()方法中的。为了让程序在未启动的情况下就能接收到广播,这里就需要使用到静态注册

    这里我们准备让程序接收一条开机广播,当收到这条广播时,就可以在onReceive()方法中执行相应的逻辑,从而实现开机启动的功能。

    新建一个类:BootCompleteReceiver,让他继承BroadcastReceiver,在onReceive()方法中简单地Toast一下,代码如下:

    public class BootCompleteReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();
        }
    }

    可以看到,这里不再使用内部类的方式来定义广播接收器,因为稍后我们需要在清单文件AndroidManifest.xml中将这个广播接收器的类名注册进去。

    然后修改清单文件AndroidManifest.xml,代码如下:

     1 <uses-sdk
     2         android:minSdkVersion="8"
     3         android:targetSdkVersion="16" />
     4     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     5     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
     6 
     7     <application
     8         android:allowBackup="true"
     9         android:icon="@drawable/ic_launcher"
    10         android:label="@string/app_name"
    11         android:theme="@style/AppTheme" >
    12         <activity
    13             android:name="com.example.m05_broadcastreceiver01.MainActivity"
    14             android:label="@string/app_name" >
    15             <intent-filter>
    16                 <action android:name="android.intent.action.MAIN" />
    17 
    18                 <category android:name="android.intent.category.LAUNCHER" />
    19             </intent-filter>
    20         </activity>
    21         
    22         <receiver android:name=".BootCompleteReceiver">
    23             <intent-filter >
    24                 <action android:name="android.intent.action.BOOT_COMPLETED"/>
    25             </intent-filter>
    26         </receiver>
    27     </application>

    代码解释如下:

    终于,<application>标签内多了个子标签<receiver>,所有的静态注册的广播接收器都是在这里进行注册的。

    22行:name中为广播接收器的名字

    24行:想要接收的广播。Android系统启动完成后,会发出这条名为android.intent.action.BOOT_COMPLETED的广播。

    05行:监听系统开机广播需要声明权限。

    运行程序后,将手机关机重启,就能收到这条广播了。

    三、发送自定义广播

    1、发送标准广播  

    新建工程文件。在发广播之前,我们先定义一个广播接收器来接收此广播才行。因此,新建一个类:MyBroadcastReceiver,让他继承BroadcastReceiver,代码如下:

    public class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
        }
    }

    这里,当MyBroadcastReceiver 收到自定义的广播时,就会执行onReceive()方法中的逻辑,弹出一个Toast。

    紧接着,要在清单文件AndroidManifest.xml中对这个广播接收器进行注册:

     1     <application
     2         android:allowBackup="true"
     3         android:icon="@drawable/ic_launcher"
     4         android:label="@string/app_name"
     5         android:theme="@style/AppTheme" >
     6         <activity
     7             android:name="com.example.m05_broadcastreceiver02.MainActivity"
     8             android:label="@string/app_name" >
     9             <intent-filter>
    10                 <action android:name="android.intent.action.MAIN" />
    11 
    12                 <category android:name="android.intent.category.LAUNCHER" />
    13             </intent-filter>
    14         </activity>
    15         
    16         <receiver android:name=".MyBroadcastReceiver">
    17             <intent-filter >
    18                 <action android:name="com.example.m05_broadcastreceiver02.MY_BROADCAST"/>
    19             </intent-filter>
    20         </receiver>
    21     </application>

    代码解释:

    18行:让MyBroadcastReceiver接收一条值为om.example.m05_broadcastreceiver02.MY_BROADCAST的广播,因此待会儿在发送广播的时候,我们就需要发出这样的一条广播。

    紧接着,修改activity.xml中的代码,添加一个按钮Button。

    然后,修改MainActivity.java中的代码,添加Button的监听事件:点击按钮时,发送广播

            Button button1=(Button)findViewById(R.id.button1);
            button1.setOnClickListener(new OnClickListener() {            
                @Override
                public void onClick(View v) {
                    Intent intent =new Intent("com.example.m05_broadcastreceiver02.MY_BROADCAST");
                    sendBroadcast(intent);
                }
            });

    总结:可以看到,点击按钮时,发送com.example.m05_broadcastreceiver02.MY_BROADCAST这条广播,这样,所有能够监听com.example.m05_broadcastreceiver02.MY_BROADCAST这条广播的广播接收器就都会同时收到消息,此时发出去的就是一条标准广播,即无序广播。所以接下来就需要讲到有序广播。

    2、发送有序广播:

    广播是一种可以跨进程的通信方式,其他应用程序是可以收到的。现在我们来发一条有序广播。

    有序广播不仅有先后顺序,而且前面的广播还可以将后面的广播截断。

    在3.1的代码基础之上,将按钮的监听事件修改如下:

    1         Button button1=(Button)findViewById(R.id.button1);
    2         button1.setOnClickListener(new OnClickListener() {            
    3             @Override
    4             public void onClick(View v) {
    5                 Intent intent =new Intent("com.example.m05_broadcastreceiver02.MY_BROADCAST");
    6                 sendOrderedBroadcast(intent, null);
    7             }
    8         });

    即将06行代码修改一下,将sendBroadcast()方法改为sendOrderedBroadcast()方法,sendOrderedBroadcast()方法接收两个参数,第二个参数是一个与权限相关的字符串,这里传入null即可。

    紧接着,修改清单文件AndroidManifest.xml中对广播接收器的注册,设置优先级:

    1         <receiver android:name=".MyBroadcastReceiver">
    2             <intent-filter android:priority="100">
    3                 <action android:name="com.example.m05_broadcastreceiver02.MY_BROADCAST"/>
    4             </intent-filter>
    5         </receiver>

    即添加第02行代码。可以看到,通过android:priority属性给广播接收器设置了优先级。这个属性的范围在-1000到1000,数值越大,优先级越高。

    接下来,如果想要拦截这个广播,防止让后面的广播接收器也接收到了这个广播。可以修改MyBroadcastReceiver中的代码:

    1 public class MyBroadcastReceiver extends BroadcastReceiver {
    2 
    3     @Override
    4     public void onReceive(Context context, Intent intent) {
    5         Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
    6         abortBroadcast();//拦截广播,防止后面的接收到
    7     }
    8 }

    即添加第06行代码。如果在onReceive()方法中调用了abortBroadcast()方法,就表示是将这条广播拦截,后面的广播接收器将无法再接收到

    特别关注:

    • 广播接收器的生命周期:关键在于BroadcastReceiver中的onReceive()方法,从onReceive()里的第一行代码开始,onReceive()里的最后一行代码结束。
    • 一个广播到来的时候,用什么方式提醒用户是最友好的呢?第一种方式是吐司,第二种方式是通知。注:不要使用对话框,以免中断了用户正在进行的操作。

    四、使用本地广播:

    之前我们发送和接收的广播全部都是属于全局广播,即发出去的广播可以被其他任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样一来,必然会造成安全问题。于是便有了本地广播:即只能在本应用程序中发送和接收广播。这就要使用到了LocalBroadcastManager这个类来对广播进行管理。

    我们修改2.1中动态注册广播接收器的代码,即修改MainActivity.java中的代码如下:

    package com.example.broadcasttest;
    
    import android.app.Activity;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.os.Bundle;
    import android.support.v4.content.LocalBroadcastManager;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
        private IntentFilter intentFilter;
    
        private LocalReceiver localReceiver;
    
        private LocalBroadcastManager localBroadcastManager;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            //通过LocalBroadcastManager的getInstance()方法得到它的一个实例
            localBroadcastManager = LocalBroadcastManager.getInstance(this);
            Button button = (Button) findViewById(R.id.button);
            button.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(
                            "com.example.broadcasttest.LOCAL_BROADCAST");
                    localBroadcastManager.sendBroadcast(intent);//调用sendBroadcast()方法发送广播
                }
            });
            //动态注册本地的广播接收器
            intentFilter = new IntentFilter();
            intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
            localReceiver = new LocalReceiver();
            localBroadcastManager.registerReceiver(localReceiver, intentFilter);
        }
        @Override
        protected void onDestroy() {
            super.onDestroy();
            localBroadcastManager.unregisterReceiver(localReceiver);
        }
    
        class LocalReceiver extends BroadcastReceiver {
            @Override
            public void onReceive(Context context, Intent intent) {
                Toast.makeText(context, "received local broadcast",
                        Toast.LENGTH_SHORT).show();
            }
        }
    }

    注:本地广播是无法通过静态注册的方式来接收的。其实也完全可以理解,因为静态注册主要就是为了让程序在未启动的情况下也能收到广播。而发送本地广播时,我们的程序肯定是已经启动了,没有必要使用到静态注册的功能。

    五、各种各样的广播: 

    在android中有很多系统自带的intent.action,通过监听这些事件我们可以完成很多功能。

      1. 开机:
        String BOOT_COMPLETED_ACTION 广播:在系统启动后。这个动作被广播一次(只有一次)。监听: “android.intent.action.BOOT_COMPLETED”
      2. 电话拨入:
        String ANSWER_ACTION 动作:处理拨入的电话。监听: “android.intent.action.ANSWER”
      3. 电量变化:
        String BATTERY_CHANGED_ACTION 广播:充电状态,或者电池的电量发生变化。监听: “android.intent.action.BATTERY_CHANGED”
      4. 日期改变:
        String DATE_CHANGED_ACTION 广播:日期被改变。 监听:“android.intent.action.DATE_CHANGED”
      5. 取消更新下载:
        String FOTA_CANCEL_ACTION 广播:取消所有被挂起的 (pending) 更新下载。 监听:“android.server.checkin.FOTA_CANCEL”
      6. 更新开始安装:
        String FOTA_READY_ACTION 广播:更新已经被下载 可以开始安装。监听 “android.server.checkin.FOTA_READY”
      7. 主屏幕:
        String HOME_CATEGORY 类别:主屏幕 (activity)。设备启动后显示的第一个 activity。 监听:"android.intent.category.HOME”
      8. 新应用:
        String PACKAGE_ADDED_ACTION 广播:设备上新安装了一个应用程序包。监听: “android.intent.action.PACKAGE_ADDED”
      9. 删除应用:
        String PACKAGE_REMOVED_ACTION 广播:设备上删除了一个应用程序包。监听: “android.intent.action.PACKAGE_REMOVED”
      10. 屏幕关闭:
        String SCREEN_OFF_ACTION 广播:屏幕被关闭。监听: “android.intent.action.SCREEN_OFF”
      11. 屏幕开启:
        String SCREEN_ON_ACTION 广播:屏幕已经被打开。 监听:“android.intent.action.SCREEN_ON”
      12. 时区改变:
        String TIMEZONE_CHANGED_ACTION 广播:时区已经改变。监听: “android.intent.action.TIMEZONE_CHANGED”
      13. 时间改变:
        String TIME_CHANGED_ACTION 广播:时间已经改变(重新设置)。 “android.intent.action.TIME_SET”
      14. 时间流逝:
        String TIME_TICK_ACTION 广播:当前时间已经变化(正常的时间流逝)。 “android.intent.action.TIME_TICK”
      15. 进入大容量存储模式:
        String UMS_CONNECTED_ACTION 广播:设备进入 USB 大容量存储模式。 “android.intent.action.UMS_CONNECTED”
      16. 退出大容量存储模式:
        String UMS_DISCONNECTED_ACTION 广播:设备从 USB 大容量存储模式退出。 “android.intent.action.UMS_DISCONNECTED”
      17. 壁纸改变:
        String WALLPAPER_CHANGED_ACTION 广播:系统的墙纸已经改变。 “android.intent.action.WALLPAPER_CHANGED”
      18. web搜索:
        String WEB_SEARCH_ACTION 动作:执行 web 搜索。 “android.intent.action.WEB_SEARCH”
      19. 网络变化:
        String CONNECTIVITY_CHANGE_ACTION 动作:网络变化。“android.intent.action.CONNECTIVITY_CHANGE_ACTION”

    六、实例:使用动态注册,监听手机的电量变化

    完整版代码如下:

    (1)activity_main.xml代码如下:

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:paddingBottom="@dimen/activity_vertical_margin"
     6     android:paddingLeft="@dimen/activity_horizontal_margin"
     7     android:paddingRight="@dimen/activity_horizontal_margin"
     8     android:paddingTop="@dimen/activity_vertical_margin"
     9     tools:context=".MainActivity" >
    10 
    11     <TextView
    12         android:id="@+id/textView1"
    13         android:layout_width="match_parent"
    14         android:layout_height="wrap_content"
    15         android:textSize="30dp"
    16         android:gravity="center"/>
    17 
    18 </LinearLayout> 

    (2)MainActivity.java的代码如下:

     1 package com.example.m05_broadcastreceiver02;
     2 
     3 import android.app.Activity;
     4 import android.content.BroadcastReceiver;
     5 import android.content.Context;
     6 import android.content.Intent;
     7 import android.content.IntentFilter;
     8 import android.os.Bundle;
     9 import android.widget.TextView;
    10 
    11 public class MainActivity extends Activity {
    12 
    13     
    14     private BatteryBroadcastReceiver batteryBroadcastReceiver;
    15     private TextView textView;
    16     @Override
    17     protected void onCreate(Bundle savedInstanceState) {
    18         super.onCreate(savedInstanceState);
    19         setContentView(R.layout.activity_main);
    20         textView=(TextView)findViewById(R.id.textView1);
    21         
    22         //动态注册监听电量的广播接收器
    23         IntentFilter intentFilter = new IntentFilter();
    24         intentFilter.addAction("android.intent.action.BATTERY_CHANGED");
    25         batteryBroadcastReceiver = new BatteryBroadcastReceiver();
    26         registerReceiver(batteryBroadcastReceiver, intentFilter);       
    27     }
    28     
    29     //取消注册监听电量的广播接收器
    30     @Override
    31     protected void onDestroy() {
    32         super.onDestroy();
    33         unregisterReceiver(batteryBroadcastReceiver);
    34     }
    35 
    36     //新建一个广播接收器,监听电量的变化
    37     public class BatteryBroadcastReceiver extends BroadcastReceiver {
    38         @Override
    39         public void onReceive(Context context, Intent intent) {
    40             if(intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
    41                 //获取当前电量
    42                 int level = intent.getIntExtra("level", 0);
    43                 //电量的总刻度
    44                 int scale = intent.getIntExtra("scale", 100);
    45                 textView.setText("电池电量为"+((level*100) / scale)+"%");
    46                 
    47                 //当电量低时,可以进行一些操作,例如弹出通知等
    48 /*                if(level<15){
    49                     do something
    50                 }*/
    51             }
    52         }
    53         
    54     }
    55     
    56 }

    紧接着,在清单文件中进行权限声明:

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

    MainActivity.java的代码解释如下:

    40至45行:固定代码,用于获取当前电量

    48至50行:当电量低时,可以进行一些操作,例如弹出通知等

    运行后,界面如下:

  • 相关阅读:
    点击鼠标获得坐标位置
    广告的字一个一个的显示出来
    纯css实现下拉菜单的效果
    用css3写出的倒三角形
    MySQL(三)
    Navicat之MySQL连接(二)
    MySQL 的安装与使用(一)
    Servlet(二)
    Servlet(一)
    Linux常用命令大全
  • 原文地址:https://www.cnblogs.com/qianguyihao/p/3960623.html
Copyright © 2011-2022 走看看