一 概述
Broadcast作为android中的四大组件,其重要性可想而知, 在android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制;这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用。android广播机制,本质上它就是一种组件间的通信方式,如果是两个组件位于不同的进程当中,那么可以用Binder机制来实现,如果两个组件是在同一个进程中,那么它们之间可以用来通信的方式就更多了,这样看来,广播机制似乎是多余的。然而,广播机制却是不可替代的,它和Binder机制不一样的地方在于,广播的发送者和接收者事先是不需要知道对方的存在的,这样带来的好处便是,系统的各个组件可以松耦合地组织在一起,这样系统就具有高度的可扩展性,容易与其它系统进行集成。
下面是广播的特性:
1.广播接收者的生命周期是非常短暂的,在接收到广播的时候创建,onReceive()方法结束之后销毁
2.广播接收者中不要做一些耗时的工作,否则会弹出Application No Response错误对话框
3.最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易被系统杀掉
4.耗时的较长的工作最好放在服务中完成
说到Broadcast,那还必须说下BroadcastReceiver,从单词意思可以看出是个广播接受器,对它的作用就是接受广播的一个组件,BroadcastReceiver是一种可以让用户接受广播通知且没有用户界面的组件,在android系统中为了响应不同的事件通知,应用程序可以注册不同的BroadcastReceiver,当BroadcastReceiver接受到通知后可以启动Activity作为响应或者通过NotificationManager提醒用户。
Broadcast也有四种不同的分类:
- 普通广播(Normal Broadcast)
- 系统广播(System Broadcast)
- 有序广播(Ordered Broadcast)
- 粘性广播(Sticky Broadcast)
- App应用内广播(Local Broadcast)
二 普通广播(Normal Broadcast)
这种广播所有监听该广播的接收器都可以监听到该广播,同一个级别的接受顺序是随机的或则说是无序的,接收器不能对收到的广播做任何处理,也不能截断广播继续传播。该种类的广播用sendBroadcast发送。下面做个简单的例子: 1.自定义一个广播接收器MyBroadcaseReceiver,集成BroadcastReceiver抽象类,并实现onReceive方法,该方法是接受到广播做的事情,在这个例子中当收到广播是Toast信息。package com.example.unorderbroadcasereceiver;作为四大组件,还要记得在AndroidManifest中注册:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
/**
* @author ZMC
*
*/
public class MyBroadcaseReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Toast.makeText(context, "Normal broadcast", Toast.LENGTH_SHORT).show();
}
}
<receiver android:name="com.example.unorderbroadcasereceiver.MyBroadcaseReceiver">2.布局文件
<intent-filter >
<action android:name="unorderbroadcast"/>
</intent-filter>
</receiver>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"这里简单的定义一个按钮,点击发送广播 3. MainActivity.java
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.unorderbroadcasereceiver.MainActivity" >
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn"
android:text="sendunordermessage"/>
</RelativeLayout>
package com.example.unorderbroadcasereceiver;上述代码中也运用了动态注册MyBroadcaseReceiver,如果使用该方法,就不用在AndroidManifest中另外注册了,但是使用该方法,要记得在onDestory方法中记得unregisterReceiver,要不然在程序退出的时候会报如下错误:
import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
/**
* @author ZMC
*
*/
public class MainActivity extends Activity {
private MyBroadcaseReceiver myBroadcaseReceiver;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myBroadcaseReceiver = new MyBroadcaseReceiver();
//方法一:动态注册
//方法二:在Androidmanifest.xml中注册
IntentFilter filter = new IntentFilter();
filter.addAction("unorderbroadcast");
registerReceiver(myBroadcaseReceiver, filter );
//按鈕
button = (Button)findViewById(R.id.btn);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent();
intent.setAction("unorderbroadcast");
sendBroadcast(intent);
}
});
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
unregisterReceiver(myBroadcaseReceiver);
}
}
4 结果
当点击按钮发送广播后,因为BroadcastReceiver一直在后台待命接收,当收到action为unorderbroadcast的广播是,就做出响应。这里就简单的Toast。
三 系统广播(System Broadcast)
android中内置了多个系统广播:只要涉及到手机的基本操作(如开机、网络状态变化、拍照等等),都会发出相应的广波,每个广播都有特定的Intent - Filter(包括具体的action),android常用系统广播action如下:
Operation | action |
---|---|
监听网络变化 | android.net.conn.CONNECTIVITY_CHANGE |
关闭或打开飞行模式 | Intent.ACTION_AIRPLANE_MODE_CHANGED |
充电时或电量发生变化 | Intent.ACTION_BATTERY_CHANGED |
电池电量低 | Intent.ACTION_BATTERY_LOW |
电池电量充足(即从电量低变化到饱满时会发出广播 | Intent.ACTION_BATTERY_OKAY |
系统启动完成后(仅广播一次) | Intent.ACTION_BOOT_COMPLETED |
按下照相时的拍照按键(硬件按键)时 | Intent.ACTION_CAMERA_BUTTON |
屏幕锁屏 | Intent.ACTION_CLOSE_SYSTEM_DIALOGS |
设备当前设置被改变时(界面语言、设备方向等) | Intent.ACTION_CONFIGURATION_CHANGED |
插入耳机时 | Intent.ACTION_HEADSET_PLUG |
未正确移除SD卡但已取出来时(正确移除方法:设置–SD卡和设备内存–卸载SD卡) | Intent.ACTION_MEDIA_BAD_REMOVAL |
插入外部储存装置(如SD卡) | Intent.ACTION_MEDIA_CHECKING |
成功安装APK | Intent.ACTION_PACKAGE_ADDED |
成功删除APK | Intent.ACTION_PACKAGE_REMOVED |
重启设备 | Intent.ACTION_REBOOT |
屏幕被关闭 | Intent.ACTION_SCREEN_OFF |
屏幕被打开 | Intent.ACTION_SCREEN_ON |
关闭系统时 | Intent.ACTION_SHUTDOWN |
重启设备 | Intent.ACTION_REBOOT |
四 有序广播(Ordered Broadcast)
android中的有序广播,也是一种比较常用的广播,该种类的广播用sendOrderedBroadcast发送。,该中广播主要有一下特性:-
按照接收者的优先顺序来接收广播,优先级别在intent-filter中的priority中声明,-
1000
到
1000
之间,值越大优先级越高,
- 可以终止广播的继续传播,接受者可以修改intent的内容。
-
同级别接收顺序是随机的,级别低的后收到
- 能截断广播的继续传播,高级别的广播接收器接收广播后能决定时候截断。能处理广播
- 同级别动态注册高于静态注册
有序广播例子: 1.自定义两个BroadcastReceiver MyBroadcastReceiver.java
package com.example.orderbroadcastreceiver;SecondBroadcastReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
public class MyBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String data = intent.getStringExtra("data");
Toast.makeText(context, data, Toast.LENGTH_SHORT).show();
Bundle bundle = new Bundle();
bundle.putString("data", "from MyBroadcastReceiver");
setResultExtras(bundle );
}
}
package com.example.orderbroadcastreceiver;2.布局文件 这里也是简单的定义一个按钮发送有序广播
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
public class SecondBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Bundle bundle = getResultExtras(false);
String data = bundle.getString("data");
Toast.makeText(context, data, Toast.LENGTH_SHORT).show();
}
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"3.MainActivity.java
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.orderbroadcastreceiver.MainActivity" >
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn"
android:text="sendordermessage"/>
</RelativeLayout>
package com.example.orderbroadcastreceiver;4.AndroidManifest.xml
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private MyBroadcastReceiver myBroadcastReceiver;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.btn);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent();
intent.putExtra("data", "from activity");
intent.setAction("orderbroadcast");
sendOrderedBroadcast(intent, null);
}
});
}
}
<?xml version="1.0" encoding="utf-8"?>在这个例子中直接使用了静态注册的方式,并分别为两个Receiver设置了优先级。 4.结果
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.orderbroadcastreceiver"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="21" />
<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="com.example.orderbroadcastreceiver.MyBroadcastReceiver">
<intent-filter android:priority="1000">
<action android:name="orderbroadcast"/>
</intent-filter>
</receiver>
<receiver android:name="com.example.orderbroadcastreceiver.SecondBroadcastReceiver">
<intent-filter android:priority="500">
<action android:name="orderbroadcast"/>
</intent-filter>
</receiver>
</application>
</manifest>
当程序之后,有MyBroadcastReceiver和SecondBroadcastReceiver在后台待命接收action为orderbroadcast的广播,点击发送按钮之后这两个广播就能先后接收到了。因为MyBroadcastReceiver的优先级为1000,SecondBroadcastReceiver的优先级为500,所以MyBroadcastReceiver先收到广播,而SecondBroadcastReceiver后接收到广播,同时SecondBroadcastReceiver接收到的是MyBroadcastReceiver处理后的广播,因此它们两个接收到的消息不一样。另外,优先级高的Receiver可以对广播进行截断,截断后比它优先级低的Receiver就不能接收到该广播了,若想截断广播,就只需在Receiver中的onReceive方法中执行abortBroadcast()方法即可。
五 粘性广播(Sticky Broadcast)
粘性广播的特点是Intent会一直保留到广播事件结束,而这种广播也没有所谓的10秒限制,10秒限制是指普通的广播如果onReceive方法执行时间太长,超过10秒的时候系统会将这个广播置为可以干掉的candidate,一旦系统资源不够的时候,就会干掉这个广播而让它不执行。该广播用sendStickyBroadcast发送。粘性广播例子: 1.自定义MyBroadcastReceiver MyBroadcastReceiver.java
package com.example.stickybroadcastdemo;2.布局文件 定义两个按钮,一个按钮发送粘性广播,一个按钮注册Receiver
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) {
// TODO Auto-generated method stub
Toast.makeText(context, "stickybroadcast", Toast.LENGTH_SHORT).show();
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"3.MainActivity.java
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.example.stickybroadcastdemo.MainActivity" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn"
android:text="sendstickybroadcast" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn1"
android:text="registerReceiver" />
</LinearLayout>
package com.example.stickybroadcastdemo;上述代码中button1按钮动态注册Receiver,button发送粘性广播,同时特别注意:发送粘性广播要使用这个api需要权限android.Manifest.permission.BROADCAST_STICKY,在AndroidManifest.xml中配置如下:
import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
/**
* @author ZMC
*
*/
public class MainActivity extends Activity {
private Button button,button1;
MyBroadcastReceiver myBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.btn);
button1 = (Button)findViewById(R.id.btn1);
//发送广播
button.setOnClickListener(new OnClickListener() {
@SuppressWarnings("deprecation")
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent("stickybroadcast");
sendStickyBroadcast(intent);
}
});
//点击注册Receiver
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
reg(v);
}
});
}
//动态注册Receiver
private void reg(View view){
IntentFilter filter = new IntentFilter();
filter.addAction("stickybroadcast");
myBroadcastReceiver = new MyBroadcastReceiver();
registerReceiver(myBroadcastReceiver, filter);
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
unregisterReceiver(myBroadcastReceiver);
}
}
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>4.结果
当点击sendbroadcast按钮是,因为没有注册Receiver,所以没有响应,这时再点击registerReceiver按钮时,动态注册Receiver,这时就显示之前发送广播的消息。因此这种广播就是Intent会一直保留到广播事件结束,即使Recceiver没有注册。
六 App应用内广播(Local Broadcast)
因为android中的广播是可以跨域的(跨App),因此可能存在一下问题:- 其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
- 其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息; 即会出现安全性 & 效率性的问题。
app test的代码
test app中只有一个MyBroadcaseReceiver来接受另一个app发送的广播: MyBroadcaseReceiver.javapackage com.example.unorderbroadcasereceiver;同时注册Receiver
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
/**
* @author ZMC
*
*/
public class MyBroadcaseReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Toast.makeText(context, "App 2:Normal broadcast", Toast.LENGTH_SHORT).show();
}
}
<receiver android:name="com.example.test.MyBroadcaseReceiver">
<intent-filter >
<action android:name="unorderbroadcast"/>
</intent-filter>
</receiver>
App 2的代码:
1.自定义一个广播接收器MyBroadcaseReceiver.package com.example.unorderbroadcasereceiver;2.布局文件
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
/**
* @author ZMC
*
*/
public class MyBroadcaseReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Toast.makeText(context, "App 2:Normal broadcast", Toast.LENGTH_SHORT).show();
}
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"这里简单的定义一个按钮,点击发送广播3. MainActivity.java
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.unorderbroadcasereceiver.MainActivity" >
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn"
android:text="sendunordermessage"/>
</RelativeLayout>
package com.example.unorderbroadcasereceiver;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
/**
* @author ZMC
*
*/
public class MainActivity extends Activity {
private MyBroadcaseReceiver myBroadcaseReceiver;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myBroadcaseReceiver = new MyBroadcaseReceiver();
//方法一:动态注册
IntentFilter filter = new IntentFilter();
filter.addAction("unorderbroadcast");
registerReceiver(myBroadcaseReceiver, filter );
//按鈕
button = (Button)findViewById(R.id.btn);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent();
intent.setAction("unorderbroadcast");
sendBroadcast(intent);
}
});
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
unregisterReceiver(myBroadcaseReceiver);
}
}
4.结果:
以上没有设置广播为App内广播,以此另一个app也能接受到广播。 5.下面将App 2中的广播改为App应用内广播,只需修改MainActivity.java中的代码即可,改后的代码如下:
package com.example.unorderbroadcasereceiver;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
/**
* @author ZMC
*
*/
public class MainActivity extends Activity {
private MyBroadcaseReceiver myBroadcaseReceiver;
LocalBroadcastManager localBroadcastManager;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myBroadcaseReceiver = new MyBroadcaseReceiver();
//方法一:动态注册
IntentFilter filter = new IntentFilter();
filter.addAction("unorderbroadcast");
//实例化LocalBroadcastManager的实例
localBroadcastManager = LocalBroadcastManager.getInstance(this);
//调用LocalBroadcastManager单一实例的registerReceiver()方法进行动态注册
localBroadcastManager.registerReceiver(myBroadcaseReceiver, filter);
//按鈕
button = (Button)findViewById(R.id.btn);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
//发送应用内广播
Intent intent = new Intent();
intent.setAction("unorderbroadcast");
localBroadcastManager.sendBroadcast(intent);
}
});
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(myBroadcaseReceiver);
}
}
需要注意的是对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册,不能静态注册。 6.结果:
. 设置广播为App应用内广播,广播就不能跨域了,一次只能在发送广播的app(App2)内接受,而另外一个app(app test)接收不到app2发送的广播。
七 总结
android中的广播分为五种:普通广播、系统广播、有序广播、粘性广播、App应用内广播,不用的广播都有不同的使用场景。BroadcastReceiver有静态注册和动态注册两种方式,当用动态注册方式时要记得销毁的时候取消注册。对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:
- 对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;
- 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
- 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。