Android入门(八)广播
原文链接:http://www.orlion.ga/572/
一、广播机制
Android中的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收到自己所关心的广播内容,这些广
播可能是来自于系统的,也可能是来自于其他应用程序的。Android提供了一套完整的 API,允许应用程序自由地发送和接收广播。发送广播的方法需要借助Intent。而接收广播的方法则需要引入一个新的概念,广播接收器(Broadcast Receiver)
广播可以分为两种类型:
-
标准广播:是一种完全异步的广播,广播发出后所有的广播接收器几乎同时收到广播
-
有序广播:是一种同步广播,广播发出后同一时刻只有一个广播能够收到这条消息,只有当这个广播接收器中的逻辑处理完之后广播才会继续传播。所以此时的广播是有先后顺序的,优先级高的广播接收器就可以先收到广播,并且前面的广播接收器可以截断正在传递的广播,这样后边的广播接收器就收不到广播了
二、接收系统广播
Android内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息。比如手机开机完成后会发出一条广播,电池的电量发生变化会发出一条广播,时间或时区发生改变也会发出一条广播等等。如果想要接收到这些广播,就需要使用广播接收器。
1、动态注册监听网络变化
注册广播的方式一般有两种,在代码中注册(动态注册)和在AndroidManifest.xml中注册(静态注册)
如何创建一个广播接收器呢?只需要创建一个类继承自BroadcastReceiver,并重写父类的onReceive()方法,这样当有广播来的时候,onReceive()方法就会执行,具体的逻辑放在这个代码中执行
先通过动态注册的方式编写一个监听网络变化的程序。创建一个BroadcastDemo项目,修改MainActivity代码:
package ga.orlion.broadcastdemo; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; import android.widget.Toast; 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) { 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(); } } } }
可以看到,我们在 MainActivity中定义了一个内部类 NetworkChangeReceiver,这个类是继承自 BroadcastReceiver的,并重写了父类的 onReceive()方法。这样每当网络状态发生变化时,onReceive()方法就会得到执行。然后观察 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()方法来实现的
另外,这里有非常重要的一点需要说明,Android系统为了保证应用程序的安全性做了规定,如果程序需要访问一些系统的关键性信息,必须在配置文件中声明权限才可以,否则程序将会直接崩溃,比如这里查询系统的网络状态就是需要声明权限的。打开AndroidManifest.xml文件,在里面加入如下权限就可以查询系统网络状态了:
2、静态注册实现开机启动
动态注册灵活性很大,但是它必须在程序启动之后才能接受广播,如果想让程序在未启动的情况下接受到广播就需要使用静态注册的方式了。
我们准备让程序接收一条开机广播,当接收到广播的时候就可以在onReceive()方法里执行相应的逻辑,从而实现开机启动的功能,新建一个BootCompleteReceiver继承自BroadcastReceiver,代码:
package ga.orlion.broadcastdemo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.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:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ga.orlion.broadcastdemo" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> <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" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".BootCompleteReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver> </application> </manifest>
需要声明权限。
三、发送自定义广播
1、发送标准广播
首先定义一个广播接收器来接收我们的广播,创建MyBroadcastReceiver继承自BroadcastReceiver,代码:
package ga.orlion.broadcastdemo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context , "接收到广播了" , Toast.LENGTH_SHORT).show(); } }
在AndroidManifest.xml中注册:
<receiver android:name=".MyBroadcastReceiver"> <intent-filter> <action android:name="ga.orlion.broadcastdemo.MY_BROADCAST" /> </intent-filter> </receiver>
然后修改MainActivity:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("ga.orlion.broadcastdemo.MY_BROADCAST"); sendBroadcast(intent); } }); }
点击按钮的时候就发送一条广播。
2、发送有序广播
广播是一种跨进程的通信,因此我们应用程序发出的广播,其他的应用程序应该也可以收到。
现在我们尝试发送有序广播
修改MainActivity:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("ga.orlion.broadcastdemo.MY_BROADCAST"); sendOrderedBroadcast(intent, null); } }); }
可以看到,发送有序广播只需要改动一行代码,即将 sendBroadcast()方法改成sendOrderedBroadcast()方法。sendOrderedBroadcast()方法接收两个参数,第一个参数仍然是Intent,第二个参数是一个与权限相关的字符串,这里传入 null就行了。
接下来在配置文件中设定广播接收器的先后顺序,AndroidManifest.xml:
<receiver android:name=".MyBroadcastReceiver"> <intent-filter android:priority="100"> <action android:name="ga.orlion.broadcastdemo.MY_BROADCAST" /> </intent-filter> </receiver>
MyBroadcastReceiver可以选择是否允许广播继续传递了。修改 MyBroadcastReceiver中的代码
package ga.orlion.broadcastdemo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context , "接收到广播了" , Toast.LENGTH_SHORT).show(); abortBroadcast(); } }
如果在 onReceive()方法中调用了 abortBroadcast()方法,就表示将这条广播截断,后面的广播接收器将无法再接收到这条广播。
四、使用本地广播
前面我们发送和接收的广播全部都是属于系统全局广播, 即发出的广播可以被其他任何的任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样就很容易会引起安全性的问题,为了能够简单地解决广播的安全性问题,Android引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递, 并且广播接收器也只能接收来自本应用
程序发出的广播。本地广播的用法并不复杂,主要就是使用了一个 LocalBroadcastManager来对广播进行
管理,并提供了发送广播和注册广播接收器的方法。
修改MainActivity:
package ga.orlion.broadcastdemo; 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.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 = LocalBroadcastManager.getInstance(this); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("ga.orlion.broadcastdemo.LOCAL_BROADCAST"); localBroadcastManager.sendBroadcast(intent); } }); intentFilter = new IntentFilter(); intentFilter.addAction("ga.orlion.broadcastdemo.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, "接收到本地广播", Toast.LENGTH_SHORT).show(); } } }
首先是通过LocalBroadcastManager的getInstance()方法得到了它的一个实例,然后在注册广播接收器的时候调用的是 LocalBroadcastManager的registerReceiver()方法,在发送广播的时候调用的是LocalBroadcastManager的sendBroadcast()
方法。