广播机制简介

Android提供了一套完整的API,允许应用程 序自由地发送和接收广播。
Android 中的广播主要可以分为两种类型:标准广播和有序广播。

  • 标准广播(Normal broadcasts )是一种完全异步执行的广播,在广播发出之后,所有的广 播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可 言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。
  • 有序广播(Ordered broadcasts )则是一种同步执行的广播,在广播发出之后,同一时刻只 会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后, 广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就 可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的 广播接收器就无法收到广播消息了。

接收系统广播

动态注册监听网络变化
注册广播的方式一般有两种,在代码中注册和AndroidManifest.xml中注册,其中前者也被称为动态注册,后者也被称为静态注册。

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

动态注册:新建一个BroadcastTest项目,然后修改MainActivity中的代码

public class MainActivity extends AppCompatActivity {private IntentFilter intentFilter;private NetworkChangeReceiver networkChangeReceiver;@Overrideprotected void onCreate(Bundle savedlnstanceState) {super.onCreate(savedlnstanceState); setContentView(R.layout.activitymain);intentFilter = new IntentFilterd; 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 {@Overridepublic 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.CONNECTIVTY_CHANGE的action,为什么要添加这个值呢?因为当 网络状态发生变化时,系统发出的正是一条值为android. net. conn.CONNECTIVITY_CHANGE的 广播,也就是说我们的广播接收器想要监听什么广播,就在这里添加相应的action。接下来创建 了一个NetworkChangeReceiver的实例,然后调用registerReceiver()方法进行注册,将NetworkChangeReceiver 的实例和 IntentFilter 的实例都传了进去,这样 NetworkChangeReceiver 就会收到所有值为android.net.conn.CONNECTIVTY_CHANGE的广播,也就实现了 监听网络变化的功能。

最后要记得,动态注册的广播接收器一定都要取消注册才行,这里我们是在onDestroyO 方法中通过调用unregisterReceiver()方法来实现的。

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

public class MainActivity extends AppCompatActivity (class NetworkChangeReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {ConnectivityManager connectionManager =(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);Networkinfo networkinfo = connectionManager.getActiveNetworkInfo(); if (networkinfo != null && networkinfo.isAvailableC)) {Toast.makeText(context, "network is available11, Toast.LENGTH_SHORT). show();} else {Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT). show();}}}}

在 onReceive()方法中,首先通过 getSystemService()方法得到了 ConnectivityManager 的实例,这是一个系统服务类,专门用于管理网络连接的。然后调用它的getActiveNetwork- Info()方法可以得到Networkinfo的实例,接着调用Networkinfo的isAvailable()方法, 就可以判断出当前是否有网络了

这里访问系统的网络状态就是需要声明权限的。打开 AndroidManifest.xml文件,在里面加入如下权限就可以访问系统网络状态了

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.broadcasttest"><uses-permission android:name=ilandroid.permission.ACCESS_NETWORK_STATE" /></manifest>

静态注册实现开机启动

动态注册的广播接收器必须要在程序启动之后才能接收到广播。
静态注册可以让程序在未启动的情况下就能接收到广播

可以使用Android Studio 的快捷方式来创建一 个广播接收器,右击 com.example.broadcasttest >New—>Other->Broadcast Receiver

我们将广播接收器命名为BootCompleteReceiver, Exported属性表示是否 允许这个广播接收器接收本程序以外的广播,Enabled属性表示是否启用这个广播接收器。勾选 这两个属性,点击Finish完成创建。
然后修改BootCompleteReceiver中的代码

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

静态的广播接收器一定要在AndroidManifest.xml文件中注册才可以使用,不过由于我 们是使用Android Studio的快捷方式创建的广播接收器,因此注册这一步已经被自动完成了。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest"><uses-permission android:name="android.permission.ACCESS NETWORK STATE" /><applicationandroid:allowBackup="true"android: icon="@mipmap/ic_launcher"android:label="@string/appname"android:supportsRtl="true"android:theme="@style/AppTheme"><receiverandroid:name=".BootCompleteReceiver"android:enabled="true"android:exported="true"></receiver></application></manifest>

可以看到,标签内出现了一个新的标签,所有静态的广播接收 器都是在这里进行注册的。
不过目前BootCompleteReceiver还是不能接收到开机广播的,我们还需要对AndroidManifest. xml文件进行修改才行,如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest"><uses-permission android:name="android.permission.ACCESS NETWORK STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><applicationandroid:allowBackup="true"android: icon="@mipmap/ic_launcher"android:label="@string/appname"android:supportsRtl="true"android:theme="@style/AppTheme"><receiverandroid:name=".BootCompleteReceiver"android:enabled="true"android:exported="true"><intent-filter><action android:name="android.intent.action.BOOT__COMPLETED" /> </intent-filter></receiver></application></manifest>

由于Android系统启动完成后会发出一条值为android. intent. action. BOOT COMPLETED 的广播,因此我们在标签里添加了相应的action.另外,监听系统开机广播也 是需要声明权限的,可以看到,我们使用标签又加入了一条android.permission.RECEIVE_BOOT_COMPLETED 权限。

发送自定义广播

发送标准广播

在发送广播之前,我们还是需要先定义一个广播接收器来准备接收此广播才行,不然发出去 也是白发。因此新建一个MyBroadcastReceiver

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

这里当MyBroadcastReceiver收到自定义的广播时,就会弹出“received in MyBroadcast- Receiver”的提示。然后在AndroidManifest.xml中对这个广播接收器进行修改:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest"><uses-permission android:name="android.permission.ACCESS NETWORK STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><applicationandroid:allowBackup="true"android: icon="@mipmap/ic_launcher"android:label="@string/appname"android:supportsRtl="true"android:theme="@style/AppTheme"><receiverandroid:name=".BootCompleteReceiver"android:enabled="true"android:exported="true"><intent-filter><action android:name="com.example.broadcasttest.MY_BROADCAST"/> </intent-filter></receiver></application></manifest>

可以看到,这里让 MyBroadcastReceiver 接收一条值为 com.example.broadcasttest.
MY_BROADCAST的广播,因此待会儿在发送广播的时候,我们就需要发出这样的一条广播。
接下来修改activity main.xml中的代码,如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent" ><Buttonandroid:id="@+id/button"and roid: layout_width=,,match_pa rent"android:layout_height="wrap_content" android:text=uSend Broadcast"/></LinearLayout>

这里在布局文件中定义了一个按钮,用于作为发送广播的触发点。然后修改MainActivity中 的代码,如下所示:

public class MainActivity extends AppCompatActivity {©Overrideprotected void onCreate(Bundle savedlnstanceState) {super.onCreate(savedlnstanceState);setContentView(R.layout.activitymain);Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {Intent intent = newIntent("com.example.broadcasttest.MY_BROADCAST"); sendBroadcast(intent);}})}

可以看到,我们在按钮的点击事件里面加入了发送自定义广播的逻辑。首先构建岀了一个 Intent对象,并把要发送的广播的值传入,然后调用了 Context的sendBroadcast()方法将广 播发送出去,这样所有监听com.example.broadcasttest.MY_BROADCAST这条广播的广播接收 器就会收到消息。此时发岀去的广播就是一条标准广播。

发送有序广播

新建一个 BroadcastTest2 项目,将项目创建好之后,还需要在这个项目下定义一个广播接收器,用于接收上一小节中的自定 义广播。新建AnotherBroadcastReceiver

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

这里仍然是在广播接收器的onReceive()方法中弹出了一段文本信息。然后在
AndroidManifest.xml中对这个广播接收器进行修改

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest"><uses-permission android:name="android.permission.ACCESS NETWORK STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><applicationandroid:allowBackup="true"android: icon="@mipmap/ic_launcher"android:label="@string/appname"android:supportsRtl="true"android:theme="@style/AppTheme"><receiverandroid:name=".AnotherBroadcastReceiver"android:enabled="true"android:exported="true"><intent-filter><action android:name="com.example.broadcasttest.MY_BROADCAST"/> </intent-filter></receiver></application></manifest>

可以看到,AnotherBroadcastReceiver 同样接收的是 com.example. broadcasttest.MY_BROADCAST这条广播。现在运行BroadcastTest2项目将这个程序安装到模拟器上,然后重新回到BroadcastTest项目的主界面,并点击一下Send Broadcast按钮,就会分别弹出两次提示信息

这样就强有力地证明了,我们的应用程序发出的广播是可以被其他的应用程序接收到的。 不过到目前为止,程序里发出的都还是标准广播,现在我们来尝试一下发送有序广播。重新
回到BroadcastTest项目,然后修改MainActivity中的代码

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedlnstanceState) {super.onCreate(savedlnstanceState); setContentView(R.layout.activitymain);Button button = (Button) findViewByld(R.id.button); button.setOnClickListener(new View.OnClickListener() { @0verride public void onClick(View v) {Intent intent = newIntent("com.example.broadcasttest.MYBROADCAST"); sendOrderedBroadcast(intent, null);}})}}

发送有序广播只需要改动一行代码,即将sendBroadcastO方法改成sendOrderedBroadcast () 方法。sendOrderedBroadcast()方法接收两个参数,第一个参数仍然是 Intent,第二个参数是一个与权限相关的字符串,这里传入null就行了。现在重新运行程序, 并点击Send Broadcast按钮,你会发现,两个应用程序仍然都可以接收到这条广播

这个时候的广播接收器是有先后顺序的, 而且前面的广播接收器还可以将广播截断,以阻止其继续传播。

那么该如何设定广播接收器的先后顺序呢?当然是在注册的时候进行设定的了。修改AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.broadcasttest"><uses-permission android:name="android.permission.ACCESS NETWORK STATE" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><applicationandroid:allowBackup="true"android: icon="@mipmap/ic_launcher"android:label="@string/appname"android:supportsRtl="true"android:theme="@style/AppTheme"><receiverandroid:name=".AnotherBroadcastReceiver"android:enabled="true"android:exported="true"><intent-filter android:priority="100"><action android:name="com.example.broadcasttest.MY_BROADCAST"/> </intent-filter></receiver></application></manifest>

可以看到,我们通过android:priority属性给广播接收器设置了优先级,优先级比较高 的广播接收器就可以先收到广播。这里将MyBroadcastReceiver的优先级设成了 100,以保证它一 定会在AnotherBroadcastReceiver之前收到广播。
既然已经获得了接收广播的优先权,那么MyBroadcastReceiver就可以选择是否允许广播继 续传递了。修改MyBroadcastReceiver中的代码

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

如果在onReceive()方法中调用了 abortBroadcast ()方法,就表示将这条广播截断,后面
的广播接收器将无法再接收到这条广播。现在重新运行程序,并点击一下Send Broadcast按钮, 你会发现,只有MyBroadcastReceiver中的Toast信息能够弹出,说明这条广播经过MyBroadcast- Receiver之后确实是终止传递了。

使用本地广播

前面我们发送和接收的广播全部属于系统全局广播,即发出的广播可以被其他任何应用程序 接收到,并且我们也可以接收来自于其他任何应用程序的广播。

为了能够简单地解决广播的安全性问题,Android引入了一套本地广播机制,使用这个机制 发岀的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出 的广播

修改MainActivity中的代码



首先是通过LocalBroadcastManager的getlnstance()方 法得到了它的一个实例,然后在注册广播接收器的时候调用的是LocalBroadcastManager的 registerReceiver ()方法,在发送广播的时候调用的是 LocalBroadcastManager 的 sendBroadcast () 方法

本地广播是无法通过静态注册的方式来接收的

广播的最佳实践一实现强制下线功能

新建一个 BroadcastBestPractice项目,先创建一个Activitycollector类用于管理所有的活动

public class ActivityCollector {public static List<Activity> activities = new ArrayList<>();public static void addActivity(Activity activity) {activities.add(activity);}public static void removeActivity(Activity activity) { activities.remove(activity);}public static void finishAll() {for (Activity activity : activities) {if (!activity.isFinishing()) {activity.finish();}}}}

然后创建BaseActivity类作为所有活动的父类

public class BaseActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedlnstanceState) { superonCreate(savedlnstanceState); ActivityCollector.addActivity(this);}@0verrideprotected void onDestroy() { super.onDestroyO; Activitycollector.removeActivity(this);)}

首先需要创建一个登录界面的活动,新建LoginActivity,并让Android Studio帮我们自 动生成相应的布局文件。然后编辑布局文件activityjogin.xml


修改LoginActivity中的代码



这里我们模拟了一个非常简单的登录功能。首先要将LoginActivity的继承结构改成继承自 BaseActivity,然后调用findViewById()方法分别获取到账号输入框、密码输入框以及登录按钮 的实例。接着在登录按钮的点击事件里面对输入的账号和密码进行判断,如果账号是admin并且 密码是123456,就认为登录成功并跳转到MainActivity,否则就提示用户账号或密码错误。
因此,你就可以将MainActivity理解成是登录成功后进入的程序主界面了,这里我们并不需 要在主界面里提供什么花哨的功能,只需要加入强制下线功能就可以了,修改aCtivity_main.xml 中的代码,如下所示:

然后修改MainActivity中的代码

public class MainActivity extends BaseActivity {@Overrideprotected void onCreate(Bundle savedlnstanceState) ( super.onCreate(savedlnstanceState); setContentView(R.layout.activitymain);Button forceOffline = (Button) findViewByld(R.id.force_offline); forceOffline.setOnClickListener(new View.OnCtickListener() { @Overridepublic void onClick(View v) { Intent intent = new Intent("com.example.broadcastbestpractice.FORCE_OFFLINEH"); sendBroadcast(intent);} })}}

不过这里有个重点,我们在按钮的点击事件里面发送了一条广播,广播的值 为 com.example.broadcastbestpractice.FORCE OFFLINE,这条广播就是用于通知程序强制 用户下线的。也就是说强制用户下线的逻辑并不是写在MainActivity里的,而是应该写在接收这 条广播的广播接收器里面,这样强制下线的功能就不会依附于任何的界面,不管是在程序的任何 地方,只需要发出这样一条广播,就可以完成强制下线的操作了。

只需要在BaseActivity中动态注册一个广播接收 器就可以了,因为所有的活动都是继承自BaseActivity的。

修改BaseActivity中的代码:

先来看一下ForceOfflineReceiver中的代码,这次onReceive()方法里可不再是仅仅弹出一 个Toast 了,而是加入了较多的代码,那我们就来仔细地看看吧。首先肯定是使用 AlertDialog.Builder来构建一个对话框,注意这里一定要调用setCancelable()方法将对话 框设为不可取消,否则用户按一下Back键就可以关闭对话框继续使用程序了。然后使用 setPositiveButton()方法来给对话框注册确定按钮,当用户点击了确定按钮时,就调用 ActivityCollector的finishAll()方法来销毁掉所有活动,并重新启动LoginActivity这个活动。
再来看一下我们是怎么注册ForceOfflineReceiver这个广播接收器的,可以看到,这里重写了 onResumeO和onPause()这两个生命周期函数,然后分别在这两个方法里注册和取消注册了 ForceOfflineReceiver .
.
接下来我们还需要对AndroidManifest.xml进行修改

更多相关文章

  1. [置顶] Android(安卓)Wi-Fi Direct 开发指南
  2. Android开发学习 -- Day11 BroadcastReceiver
  3. android底层与zigbee通信
  4. Android中本地广播的实现
  5. android BroadcastReceiver 注册方式
  6. android 广播的总结
  7. Android温故之-BroadcastReceiver
  8. Android常见的Event-driven方案
  9. Android基于MQTT实现发布消息与监听topic接收消息

随机推荐

  1. Android中ExpandableListView的使用
  2. Android通过http协议POST传输方式(输出流
  3. 善用Android预定义样式
  4. android 将图片转换成黑白图片
  5. Android(安卓)SDK 源码解析项目
  6. Android(安卓)WebView相关属性
  7. Android的WindowManager解析
  8. Android(安卓)layout
  9. Android布局概览
  10. android设置背景半透明效果