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()方法运行了较长时间而没有结束时,程序就会报错。
因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或
者启动一个服务等.

android: 接收系统广播的更多相关文章

  1. Android 接收系统广播(动态和静态)

    1.标准广播:是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎会在同一时刻接收到这条广播信息,它们之间没有先后顺序.效率高.无法被截断. 2.有序广播:是一种同步执行的广播,在广播发出后 ...

  2. Android接收系统广播

    Android内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息.比如手机开机完成后会发出一条广播,电池的电量发生变化会发出一条广播,时间或时区发生改变也会发出一条 ...

  3. Android开发之接收系统广播消息

    BroadcastReceiver除了接收用户所发送的广播消息之外.另一个重要的用途:接收系统广播. 假设应用须要在系统特定时刻运行某些操作,就能够通过监听系统广播来实现.Android的大量系统事件 ...

  4. android接收短信——framework处理流程(android 5.1)

    modem层不懂,所以直接从RIL.java开始.以电信卡接收短信为例 modem通知RIL.java中的 RILReceiver处理接收信息 class RILReceiver implements ...

  5. Android接收短信

    Android收到短信时会广播android.provider.Telephony.SMS_RECEIVED消息,因此只要定义一个Receiver,收听该消息,就能接收短信. <receiver ...

  6. android: 接收和发送短信

    8.2    接收和发送短信 收发短信应该是每个手机最基本的功能之一了,即使是许多年前的老手机也都会具备这 项功能,而 Android 作为出色的智能手机操作系统,自然也少不了在这方面的支持.每个 A ...

  7. Android常用系统广播

    关于Intent的使用,可参阅前章:http://www.cnblogs.com/caidupingblogs/p/5101669.html //关闭或打开飞行模式时的广播 Intent.ACTION ...

  8. Android 接收短信

    启动程序时启动一个service,在service里注册接收短信的广播,当手机收到短信里,打印出短信内容跟电话号码. package com.lmy.SmsListener; import andro ...

  9. Android接收wifi路由器发送过来的一组字节数据

    1.字节数组转换为字符串 byte[] byBuffer = new byte[20];... ...String strRead = new String(byBuffer);strRead = S ...

随机推荐

  1. python文件、文件夹操作OS模块

    转自:python文件.文件夹操作OS模块   '''一.python中对文件.文件夹操作时经常用到的os模块和shutil模块常用方法.1.得到当前工作目录,即当前Python脚本工作的目录路径: ...

  2. 自适应电脑、手机和iPad的网页设计方法

    随着3G的普及,越来越多的人使用手机上网. 移动设备正超过桌面设备,成为访问互联网的最常见终端.于是,网页设计师不得不面对一个难题:如何才能在不同大小的设备上呈现同样的网页? 手机的屏幕比较小,宽度通 ...

  3. C++ code:浮点数的比较(Floating-Pointing Number Comparison)

    浮点数可以进行比较,但是浮点数由于表示精度在不同浮点数类型中的差异,所以会被误用.例如: #include <iostream> using namespace std; int main ...

  4. ASP.NET Global.asax详解【转】

    global.asax是一个文本文件,它提供全局可用代码.这些代码包括应用程序的事件处理程序以及会话事件.方法和静态变量.有时该文件也被称为应用程序文件. global.asax 文件中的任何代码都是 ...

  5. 20155309南皓芯 网络对抗《网络攻防》 Exp1 PC平台逆向破解(5)M

    实践目标 本次实践的对象是linux的可执行文件 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串. 该程序同时包含另一个代码片段,getShell,会返回一个可 ...

  6. 深度优先搜索(DFS)和广度优先搜索(BFS)

    深度优先搜索(DFS) 广度优先搜索(BFS) 1.介绍 广度优先搜索(BFS)是图的另一种遍历方式,与DFS相对,是以广度优先进行搜索.简言之就是先访问图的顶点,然后广度优先访问其邻接点,然后再依次 ...

  7. kotlin 插件更新到 1.2.41 程序出错 Please use kotlin-stdlib-jdk7 instead

    buildscript { ext.kotlin_version = '1.2.41' repositories { google() jcenter() } dependencies { class ...

  8. [ZJOI2012]旅游

    题目: 这题意...还以为他说的线段是路径 写了好久的dp..写不出来 看了网上的题解..才知道就是两点连线 然后就是一般的平面图转对偶图的思想 然后算一下边数发现是颗树,求一下直径就好了 代码: # ...

  9. BZOJ4997 [Usaco2017 Feb]Why Did the Cow Cross the Road III

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ4997 题意概括 在n*n的区域里,每一个1*1的块都是一个格子. 有k头牛在里面. 有r个篱笆把格 ...

  10. 6-5 移动的盒子 uva12657

    较为复杂的一题:有点类似6-1  但是分析完之后比6-1简单   就是按照思路模拟就好! 学会了双向链表   先初始化   link是关键 分析命令   可以大大简化代码  : 反转链表不用反转  改 ...