转载自 http://www.cnblogs.com/smyhvae/
广播接收器可以自由地对自己感兴趣的广播进行注册,这样当有相应的广播发出时,广播接收器就能收到该广播,并在内部处理相应的逻辑。注册广播的方式有两种,在代码中注册和在清单文件中注册,前者称为 动态注册 ,后者称为 静态注册 。
新建工程文件,首先在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系统所有的可声明的权限。
现在运行程序,就可以了。
上方程序完整版代码如下:
View Code
动态注册的方式比较灵活,但缺点是:必须在程序启动之后才能接收到广播,因为注册的逻辑是写在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行:监听系统开机广播需要声明权限。
运行程序后,将手机关机重启,就能收到这条广播了。
新建工程文件。在发广播之前,我们先定义一个广播接收器来接收此广播才行。因此,新建一个类: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 这条广播的广播接收器就都会 同时 收到消息 ,此时发出去的就是一条标准广播,即无序广播。所以接下来就需要讲到有序广播。
广播是一种可以跨进程的通信方式,其他应用程序是可以收到的。现在我们来发一条有序广播。
有序广播不仅有先后顺序,而且前面的广播还可以将后面的广播截断。
在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()方法,就表示是 将这条广播拦截,后面的广播接收器将无法再接收到 。
特别关注:
之前我们发送和接收的广播全部都是属于全局广播,即发出去的广播可以被其他任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样一来,必然会造成安全问题。于是便有了本地广播:即 只能在本应用程序中发送和接收广播 。这就要使用到了 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)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行:当电量低时,可以进行一些操作,例如弹出通知等
运行后,界面如下: