广播(Broadcast)是一种广泛运用的在应用程序之间传输信息的机制,在 Android 里面有各种各样的广播,比如电池的使用状态,电话的接收和短信的接收都会产生一个广播,应用程序也可以接受广播并做出程序逻辑上的处理, 比如我们需要让应用程序开机自动启动,其实就是应用了广播的只是,让应用程序监听接收系统开机广播来启动程序,我后面会以这个例子来具体看下广播的使用,还有比如手机电量低于20%的情况下出现一个提醒的对话框也是利用广播的原理。
关于广播,我们需要掌握了解广播的三要素:
1.广播(Broadcast) - 用于发送广播;
2.广播接收器(BroadcastReceiver) - 用于接收广播;
3.意图(Intent)-用于保存广播相关信息的媒介。
先简单介绍一下广播中需要掌握的一些概念,后面给出在代码里面的用法。
1.普通广播
普通广播是完全异步的,可以在同一时刻(逻辑上)被所有广播接收者接收到,消息传递的效率比较高,但缺点是接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播。
2.有序广播
有序广播是按照接收者声明的优先级别(声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围:-1000到1000。也可以调用IntentFilter对象的setPriority()进行设置),被接收者依次接收广播。如:A的级别高于B, B的级别高于C,那么,广播先传给A,再传给B,最后传给C。A得到广播后,可以往广播里存入数据,当广播传给B时,nB可以从广播中得到A存入的数据。
3系统广播
系统广播是Android内置的广播,满足一定条件后系统会自动发送这个广播,不需要我们再定义发送,只需要用的时候接收就可以了,比如手机开机完成后会发出一条广播,电池的电量发生变化会发出一 条广播,时间或时区发生改变也会发出一条广播,摄像头按被按下会触发广播等等。
4自定义广播
自定义广播是用户可以自己定义发送一个广播,看到很多书籍都是这么讲,可以我一直在思考为什么需要自定义广播,或者说自定义广播在Android程序中一般是用在什么地方,因为广播是为了给不同的组件中通信而使用的例如service 与 Activity 不同的进程中,或者不同的应用程序中就会使用广播机制来通信的。
5本地广播
系统广播和自定义广播都属于全局广播,即发出的广播可以被其他任何 的任何应用程序接收到, 并且我们也可以接收来自于其他任何应用程序的广播。这样就很容易会引起安全性的问题, 比如说我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。
为了能够简单地解决广播的安全性问题,Android 引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。
6动态注册广播
动态注册就是在程序中使用Context.registerReceiver注册。动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在 onCreate()方法中的。
7静态注册广播
在AndroidManifest.xml中注册广播称之为静态注册,静态注册是常驻型 ,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行,所以一些系统级别的广播比如开机启动广播就必须要用到静态注册的方法,而有些系统广播也是可以动态注册的,比如电量变化广播等可以在程序启动后监听即可。
以下我们以一些具体的例子来讲解下广播的用法,先看让一个应用程序接收开机广播的例子:
需要先对自己感兴趣的广播进行注册(这里当然是开机广播),正如前面说的这种情况只能在AndroidManifest.xml使用静态注册的方式,所有静态注册的广播接收器都是在receiver标签进行注册的,下面给出AndroidManifest.xml文件的代码以及相关使用注释:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidstudybroadcastreceive"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="22" />
//监听系统开机广播也是需要声明权限的,需要添加如下权限
<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标签进行注册的
//需要将将广播接收器的类名注册进来,我是新建了一个类MyBroadcastReceive
<receiver android:name=".MyBroadcastReceive">
<intent-filter>
//通过android:name来指定具体注册哪一个广播接收器
<action android:name="android.intent.action.BOOT_COMPLETED" />
intent-filter>
receiver>
application>
manifest>
注册后需要创建一个广播接收器,其实只需要新建一个类, 让它继承自BroadcastReceiver, 并重写父类的onReceive()方法就行了。这样当有广播到来时,onReceive()方法就会得到执行, 具体的逻辑就可以在这个方法中处理。下面给出我新建的类MyBroadcastReceive里面的代码如下:
package com.example.androidstudybroadcastreceive;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//打印一个字符串启动完成
Toast.makeText(context, "启动完成", Toast.LENGTH_LONG).show();
}
}
这里启动模拟器Genymotion完成后会看到有输出启动完成四个字,说明上面这个应用程序成功接收到了开机广播并完成了onReceive()方法中的Toast方法。
上面的例子中我们用到的知识点是系统广播以及静态注册广播,我们再来看一自定义广播通过静态注册的例子:
首先,和系统广播一样,先在AndroidManifest.xml文件中添加一个自定义广播:
<receiver android:name=".MyBroadcastReceive">
<intent-filter>
//通过android:name来指定具体注册哪一个广播接收器,这里可以自己任意指定name
<action android:name="com.dxy.mybroadcast" />
intent-filter>
receiver>
然后在onCreate()方法中点击Button发送广播:
public class MainActivity extends Activity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button) findViewById(R.id.bt);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction("com.dxy.mybroadcast");
//携带自定义的一些数据
intent.putExtra("name", "dxy");
sendBroadcast(intent);
}
});
}
}
最后接收这个广播并完成逻辑代码的编写:
public class MyBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//获取广播过来的数据
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第一个程序收到了广播,name为 "+name);
}
}
再来看动态注册的方式发送自定义广播,并且了解有序广播,普通广播的概念。
先来看下有序广播, 这里以2个Application为例:
Application1中我们需要发送广播以及接收广播:
注册广播:使用Context.registerReceiver注册。
发送广播:通过Context.sendBroadcast来发送,由Intent来传递注册时用到的Action。
接收广播:当发送的广播被接收器监听到后,会调用onReceive()方法,并将包含消息的Intent对象传回。
取消广播:动态注册的广播接收器一定都要取消注册才行。
package com.example.androidstudybroadcastreceive;
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.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private Button button;
//定义意图过滤器
private IntentFilter filter;
//定义广播接收器
BroadcastReceiveTest myBroadCast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button) findViewById(R.id.bt);
//实例化广播接收器
myBroadCast = new BroadcastReceiveTest();
//实例化意图过滤器
filter = new IntentFilter();
//为过滤器添加一个定义的广播,当然这里也可以填系统广播
filter.addAction("com.dxy.myBroadCast");
//可以通过setPriority()设置动态广播的优先级,优先级取值范围是-1000到1000
//注意的是的静态注册的广播要快于动态注册的广播,不管动态注册的优先级设置的多高,不管静态注册的优先级有多低
filter.setPriority(1000);
//注册广播
registerReceiver(myBroadCast, filter);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//首先构建出了一 个 Intent 对象,并把要发送的广播的值传入
Intent intent = new Intent("com.dxy.myBroadCast");
//携带自定义的一些数据
intent.putExtra("name", "dxy");
//调用sendBroadcast()方法发送普通广播
sendBroadcast(intent);
}
});
}
//在onDestroy()方法中调用unregisterReceiver()取消广播注册,当然也可以放在onStop()方法中取消,如果不取消注册程序退出是会报错的。
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myBroadCast);
}
}
//这里直接用了一个内部类来接收广播,当然也可以另外用一个独立的外部内来接收
class BroadcastReceiveTest extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//获取广播过来的数据
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第一个程序收到了广播,name为 "+name);
}
}
Application2中我们要做的是接收Application1中发送的广播,代码比较简单:
先注册一个广播:
<receiver android:name=".AnotherBroadcastReceive">
<intent-filter>
//通过android:name来指定具体注册监听Application1中的广播com.dxy.myBroadCast
<action android:name="com.dxy.myBroadCast" />
intent-filter>
receiver>
然后新建一个类MyBroadcastReceive类继承BroadcastReceiver重写onReceive()方法:
package com.example.androidstudybroadcastreceuve2;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;
public class AnotherBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第二个程序收到了广播,name为 "+name);
}
}
此时我们运行Application1并点击按钮发送一个广播,Logcat的截图如下:
我们可以看到Application1和Application2都接收到了这个广播并且都做了相应的处理,但是Application2确要比Application1优先收到广播,是因为静态注册的广播要快于动态注册的广播,我们下面将Application1中注册方式改为静态注册的方式并且设置优先级为1000:
<receiver android:name=".MyBroadcastReceive">
<intent-filter android:priority="1000">
//通过android:name来指定具体注册哪一个广播接收器
<action android:name="com.dxy.myBroadCast" />
intent-filter>
receiver>
Application2中设置优先级为999:
<receiver android:name=".AnotherBroadcastReceive">
<intent-filter android:priority="999">
//通过android:name来指定具体注册哪一个广播接收器
<action android:name="com.dxy.myBroadCast" />
intent-filter>
receiver>
再运行看下Logcat运行的情况Application1比Application2先收到广播:
这里再强调一点是的普通广播发送后都是可以收到的,不能截取后终止,比如我在Application1中onReceive()方法调用abortBroadcast()方法终止广播Application2还是可以收到的。
现在我们来看下有序广播,其实有序广播只需要将Application1中的sendBroadcast()方法改成sendOrderedBroadcast()就可以了,但是需要注意的是sendOrderedBroadcast()方法需要传入两个参数:
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//首先构建出了一 个 Intent 对象,并把要发送的广播的值传入
Intent intent = new Intent("com.dxy.myBroadCast");
//携带自定义的一些数据
intent.putExtra("name", "dxy");
//调用sendBroadcast()方法发送普通广播
//sendBroadcast(intent);
//调用sendOrderedBroadcast()方法发送普通广播
sendOrderedBroadcast(intent,null);
}
});
我们在Application1中的onReceive()方法中调用abortBroadcast()方法结束广播:
public class MyBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第一个程序收到了广播,name为 "+name);
abortBroadcast();
}
}
这个时候运行程序点击发送广播就只有Application1能收到广播了:
另外,因为我们的Application1现获取广播,我们可以往广播里存入数据传给下一个广播Application2
Application1广播里存入数据:
public class MyBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第一个程序收到了广播,name为 "+name);
Bundle bundle = new Bundle();
bundle.putString("next_receiver", "Application1广播传过来的");
setResultExtras(bundle);
}
}
Application2广播里接收数据:
public class MyBroadcastReceive extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getExtras().getString("name");
Bundle bundle = getResultExtras(true);
String content = bundle.getString("next_receiver");
Log.d("mybroadcast","第二个程序收到了了第一个程序发送过来的数据 "+content);
}
}
最后简单看下本地广播,本地广播是提供LocalBroadcastManager来管理的,需要注意的是,本地广播是无法通过静态注册的方式来接收的。因为静态注册主要就是为了让程序在未启动的情况下也能收到广播, 而发送本地广播时,我们的程序肯定是已经启动了,因此也完全不需要使用静态注册的功能。
这里直接给出代码:
package com.example.androidstudybroadcastreceive;
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.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private Button button;
private IntentFilter filter;
BroadcastReceiveTest myBroadCast;
private LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button) findViewById(R.id.bt);
//通过 LocalBroadcastManager 的 getInstance() 方法得到了它的一个实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
myBroadCast = new BroadcastReceiveTest();
filter = new IntentFilter();
//过滤条件的广播
filter.addAction("com.dxy.myBroadCast");
//注册广播接收器
localBroadcastManager.registerReceiver(myBroadCast, filter);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.dxy.myBroadCast");
intent.putExtra("name", "dxy");
localBroadcastManager.sendBroadcast(intent);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(myBroadCast);
}
}
class BroadcastReceiveTest extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getExtras().getString("name");
Log.d("mybroadcast","第一个程序收到了广播,name为 "+name);
}
}