android四大组件之Broadcast(广播)使用详解

时间:2021-08-18 10:31:59

一 概述

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;

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();
}

}
作为四大组件,还要记得在AndroidManifest中注册:
<receiver android:name="com.example.unorderbroadcasereceiver.MyBroadcaseReceiver">
<intent-filter >
<action android:name="unorderbroadcast"/>
</intent-filter>
</receiver>
2.布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
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>
这里简单的定义一个按钮,点击发送广播
3. MainActivity.java
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();

//方法一:动态注册
//方法二:在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);
}


}
上述代码中也运用了动态注册MyBroadcaseReceiver,如果使用该方法,就不用在AndroidManifest中另外注册了,但是使用该方法,要记得在onDestory方法中记得unregisterReceiver,要不然在程序退出的时候会报如下错误:
android四大组件之Broadcast(广播)使用详解

4 结果
android四大组件之Broadcast(广播)使用详解

当点击按钮发送广播后,因为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
因为系统广播比较简单这里就不写例子了,当使用系统广播时,只需要在注册广播接收者时定义相关的action即可,并不需要手动发送广播,当系统有相关操作时会自动进行系统广播。

四 有序广播(Ordered Broadcast)

android中的有序广播,也是一种比较常用的广播,该种类的广播用sendOrderedBroadcast发送。,该中广播主要有一下特性:
  1. 按照接收者的优先顺序来接收广播,优先级别在intent-filter中的priority中声明,-10001000之间,值越大优先级越高,
  2. 可以终止广播的继续传播,接受者可以修改intent的内容。
  3. 同级别接收顺序是随机的,级别低的后收到
  4. 能截断广播的继续传播,高级别的广播接收器接收广播后能决定时候截断。能处理广播
  5. 同级别动态注册高于静态注册

有序广播例子:
1.自定义两个BroadcastReceiver MyBroadcastReceiver.java
package com.example.orderbroadcastreceiver;

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 );

}

}
SecondBroadcastReceiver.java
package com.example.orderbroadcastreceiver;

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();
}

}
2.布局文件
这里也是简单的定义一个按钮发送有序广播
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
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>
3.MainActivity.java
package com.example.orderbroadcastreceiver;

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);
}
});
}
}
4.AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<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>
在这个例子中直接使用了静态注册的方式,并分别为两个Receiver设置了优先级。
4.结果 android四大组件之Broadcast(广播)使用详解

当程序之后,有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;

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();
}

}
2.布局文件
定义两个按钮,一个按钮发送粘性广播,一个按钮注册Receiver
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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>
3.MainActivity.java
package com.example.stickybroadcastdemo;


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);
}
}
上述代码中button1按钮动态注册Receiver,button发送粘性广播,同时特别注意:发送粘性广播要使用这个api需要权限android.Manifest.permission.BROADCAST_STICKY,在AndroidManifest.xml中配置如下:
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
4.结果
android四大组件之Broadcast(广播)使用详解

当点击sendbroadcast按钮是,因为没有注册Receiver,所以没有响应,这时再点击registerReceiver按钮时,动态注册Receiver,这时就显示之前发送广播的消息。因此这种广播就是Intent会一直保留到广播事件结束,即使Recceiver没有注册。

六 App应用内广播(Local Broadcast)

因为android中的广播是可以跨域的(跨App),因此可能存在一下问题:
  • 其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
  • 其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息; 即会出现安全性 & 效率性的问题。
对于上述情况就出现了这种广播,App应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App。相比于全局广播(普通广播),App应用内广播优势体现在:安全性高效率高。
因为要测试两个app,所以下面这个例子基于普通广播的那个项目,普通广播的那个项目发送广播,同时有两个app里都有一个action为unorderbroadcast的Receiver待命接收。 没有设置App应用广播的情况:

app test的代码

test app中只有一个MyBroadcaseReceiver来接受另一个app发送的广播: MyBroadcaseReceiver.java
package com.example.unorderbroadcasereceiver;

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
<receiver android:name="com.example.test.MyBroadcaseReceiver">
<intent-filter >
<action android:name="unorderbroadcast"/>
</intent-filter>
</receiver>

App 2的代码:

1.自定义一个广播接收器MyBroadcaseReceiver.
package com.example.unorderbroadcasereceiver;

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();
}

}
2.布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
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>
这里简单的定义一个按钮,点击发送广播
3. MainActivity.java
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.结果:
android四大组件之Broadcast(广播)使用详解

以上没有设置广播为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.结果: android四大组件之Broadcast(广播)使用详解
. 设置广播为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。