一 概述
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)
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>这里简单的定义一个按钮,点击发送广播
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,要不然在程序退出的时候会报如下错误:
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)
-
按照接收者的优先顺序来接收广播,优先级别在intent-filter中的priority中声明,-
1000
到
1000
之间,值越大优先级越高,
- 可以终止广播的继续传播,接受者可以修改intent的内容。
-
同级别接收顺序是随机的,级别低的后收到
- 能截断广播的继续传播,高级别的广播接收器接收广播后能决定时候截断。能处理广播
- 同级别动态注册高于静态注册
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设置了优先级。
当程序之后,有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发送。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.布局文件
<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.结果
当点击sendbroadcast按钮是,因为没有注册Receiver,所以没有响应,这时再点击registerReceiver按钮时,动态注册Receiver,这时就显示之前发送广播的消息。因此这种广播就是 Intent会一直保留到广播事件结束,即使Recceiver没有注册。
六 App应用内广播(Local Broadcast)
因为android中的广播是可以跨域的(跨App),因此可能存在一下问题:- 其他App针对性发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收广播并处理;
- 其他App注册与当前App一致的intent-filter用于接收广播,获取广播具体信息; 即会出现安全性 & 效率性的问题。
app test的代码
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的代码:
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(); } }
<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>这里简单的定义一个按钮,点击发送广播
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); } }
以上没有设置广播为App内广播,以此另一个app也能接受到广播。
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动态注册,不能静态注册。
七 总结
对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:
- 对于静态注册(全局+应用内广播),回调onReceive(context, intent)中的context返回值是:ReceiverRestrictedContext;
- 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity Context;
- 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context, intent)中的context返回值是:Application Context。