android: 接收系统广播

Android 内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到 各种系统的状态信息。比如手机开机完成后会发出一条广播,电池的电量发生变化会发出一 条广播,时间或时区发生改变也会发出一条广播等等。如果想要接收到这些广播,就需要使 用广播接收器,下面我们就来看一下它的具体用法。

5.2.1    动态注册监听网络变化

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

那么该如何创建一个广播接收器呢?其实只需要新建一个类,让它继承自 BroadcastReceiver, 并重写父类的 onReceive()方法就行了。这样当有广播到来时,onReceive()方法就会得到执行, 具体的逻辑就可以在这个方法中处理。

那我们就先通过动态注册的方式编写一个能够监听网络变化的程序,借此学习一下广播 接收器的基本用法吧。新建一个 BroadcastTest 项目,然后修改 MainActivity 中的代码,如下 所示:

 public class MainActivity extends Activity {

private IntentFilter intentFilter;

private NetworkChangeReceiver networkChangeReceiver;

 

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter = new IntentFilter();

intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");

networkChangeReceiver = new NetworkChangeReceiver();

registerReceiver(networkChangeReceiver, intentFilter);

}

 

@Override

protected void onDestroy() {

super.onDestroy();

unregisterReceiver(networkChangeReceiver);

}

 

class NetworkChangeReceiver extends BroadcastReceiver {

 

@Override

public void onReceive(Context context, Intent intent) {

 

Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();

}

}

 

}

可以看到,我们在 MainActivity 中定义了一个内部类 NetworkChangeReceiver,这个类 是继承自 BroadcastReceiver 的,并重写了父类的 onReceive()方法。这样每当网络状态发生变 化时,onReceive()方法就会得到执行,这里只是简单地使用 Toast 提示了一段文本信息。

然后观察 onCreate()方法,首先我们创建了一个 IntentFilter 的实例,并给它添加了一个 值为 android.net.conn.CONNECTIVITY_CHANGE 的 action,为什么要添加这个值呢?因为 当网络状态发生变化时,系统发出的正是一条值为 android.net.conn.CONNECTIVITY_ CHANGE 的广播,也就是说我们的广播接收器想要监听什么广播,就在这里添加相应的 action 就行了。接下来创建了一个 NetworkChangeReceiver 的实例,然后调用 registerReceiver() 方法进行注册,将 NetworkChangeReceiver 的实例和 IntentFilter 的实例都传了进去,这样 NetworkChangeReceiver 就会收到所有值为 android.net.conn.CONNECTIVITY_CHANGE 的广 播,也就实现了监听网络变化的功能。

最后要记得,动态注册的广播接收器一定都要取消注册才行,这里我们是在 onDestroy()方法中通过调用 unregisterReceiver()方法来实现的。 整体来说,代码还是非常简单的,现在运行一下程序。首先你会在注册完成的时候收到一条广播,然后按下 Home 键回到主界面(注意不能按 Back 键,否则 onDestroy()方法会执 行),接着按下 Menu 键→System settings→Data usage 进入到数据使用详情界面,然后尝试着 开关 Mobile Data 来启动和禁用网络,你就会看到有 Toast 提醒你网络发生了变化。

不过只是提醒网络发生了变化还不够人性化,最好是能准确地告诉用户当前是有网络还 是没有网络,因此我们还需要对上面的代码进行进一步的优化。修改 MainActivity 中的代码, 如下所示:

 

public class MainActivity extends Activity {

……

class NetworkChangeReceiver extends BroadcastReceiver {

 

@Override

public void onReceive(Context context, Intent intent) {

ConnectivityManager connectionManager = (ConnectivityManager)

getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();

 

if (networkInfo != null && networkInfo.isAvailable()) { Toast.makeText(context, "network is available",

Toast.LENGTH_SHORT).show();

} else {

Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();

}

}

 

}

}

在 onReceive()方法中,首先通过 getSystemService()方法得到了 ConnectivityManager 的 实例,这是一个系统服务类,专门用于管理网络连接的。然后调用它getActiveNetworkInfo() 方法可以得到 NetworkInfo 的实例,接着调用 NetworkInfo 的 isAvailable()方法,就可以判断 出当前是否有网络了,最后我们还是通过 Toast 的方式对用户进行提示。

另外,这里有非常重要的一点需要说明,Android 系统为了保证应用程序的安全性做了 规定,如果程序需要访问一些系统的关键性信息,必须在配置文件中声明权限才可以,否则 程 序 将 会 直 接 崩 溃 , 比 如 这 里 查 询 系 统 的 网 络 状 态 就 是 需 要 声 明 权 限 的 。 打 开 AndroidManifest.xml 文件,在里面加入如下权限就可以查询系统网络状态了:

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest"

android:versionCode="1" android:versionName="1.0" >

<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />

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

……

</manifest>

访问 http://developer.android.com/reference/android/Manifest.permission.html 可以查看 Android

系统所有可声明的权限。

现在重新运行程序,然后按下 Home 键→按下 Menu 键→System settings→Data usage 进 入到数据使用详情界面,关闭 Mobile Data 会弹出无网络可用的提示,如图 5.3 所示。

图   5.3

 

然后重新打开 Mobile Data 又会弹出网络可用的提示,如图 5.4 所示。

 

图   5.4

 

 

5.2.2    静态注册实现开机启动

 

动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是 它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在 onCreate()方法中的。那么有没有什么办法可以让程序在未启动的情况下就能接收到广播 呢?这就需要使用静态注册的方式了。

这里我们准备让程序接收一条开机广播,当收到这条广播时就可以在 onReceive()方法里 执行相应的逻辑,从而实现开机启动的功能。新建一个 BootCompleteReceiver 继承自 BroadcastReceiver,代码如下所示:

 

public class BootCompleteReceiver extends BroadcastReceiver {

 

@Override

public void onReceive(Context context, Intent intent) { Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();

}

 

}

可以看到,这里不再使用内部类的方式来定义广播接收器,因为稍后我们需要在 AndroidManifest.xml 中将这个广播接收器的类名注册进去。在 onReceive()方法中,还是简单 地使用 Toast 弹出一段提示信息。

然后修改 AndroidManifest.xml 文件,代码如下所示:

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest"

android:versionCode="1" android:versionName="1.0" >

……

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

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

<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" >

……

<receiver android:name=".BootCompleteReceiver" >

<intent-filter>

<action android:name="android.intent.action.BOOT_COMPLETED" />

</intent-filter>

</receiver>

</application>

</manifest>

终于,<application>标签内出现了一个新的标签<receiver>,所有静态注册的广播接收器 都是在这里进行注册的。它的用法其实和<activity>标签非常相似,首先通过 android:name 来指定具体注册哪一个广播接收器,然后在<intent-filter>标签里加入想要接收的广播就行了, 由于 Android 系统启动完成后会发出一条值为 android.intent.action.BOOT_COMPLETED 的广 播,因此我们在这里添加了相应的 action。

另外,监听系统开机广播也是需要声明权限的,可以看到,我们使用<uses-permission>标签又加入了一条 android.permission.RECEIVE_BOOT_COMPLETED 权限。 现在重新运行程序后,我们的程序就已经可以接收开机广播了,首先打开到应用程序管理界面来查看一下当前程序所拥有的权限。在桌面按下 Menu 键→System settings→Apps,然 后点击 BroadcastTest,如图 5.5 所示。

 

图   5.5

 

可以看到,我们的程序目前拥有访问网络状态和开机自动启动的权限。然后将模拟器关 闭并重新启动,在启动完成之后就会收到开机广播了,如图 5.6 所示。

图   5.6

 

到目前为止,我们在广播接收器的 onReceive()方法中都只是简单地使用 Toast 提示了一 段文本信息,当你真正在项目中使用到它的时候,就可以在里面编写自己的逻辑。需要注意 的是,不要在 onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为在广播接收 器中是不允许开启线程的,当 onReceive()方法运行了较长时间而没有结束时,程序就会报错。 因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或 者启动一个服务等.

posted @ 2016-02-03 08:49  dodo-yufan  阅读(1163)  评论(0编辑  收藏  举报