首页 > 代码库 > Android总结篇系列:Android广播机制
Android总结篇系列:Android广播机制
1.Android广播机制概述
Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器)。广播作为Android组件间的通信方式,可以使用的场景如下:
1.同一app内部的同一组件内的消息通信(单个或多个线程之间);
2.同一app内部的不同组件之间的消息通信(单个进程);
3.同一app具有多个进程的不同组件之间的消息通信;
4.不同app之间的组件之间消息通信;
5.Android系统在特定情况下与App之间的消息通信。
从实现原理看上,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。因此,从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。具体实现流程要点粗略概括如下:
1.广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;
2.广播发送者通过binder机制向AMS发送广播;
3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;
4.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。
对于不同的广播类型,以及不同的BroadcastReceiver注册方式,具体实现上会有不同。但总体流程大致如上。
由此看来,广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心。广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。显然,整体流程与EventBus非常类似。
在上文说列举的广播机制具体可以使用的场景中,现分析实际应用中的适用性:
第一种情形:同一app内部的同一组件内的消息通信(单个或多个线程之间),实际应用中肯定是不会用到广播机制的(虽然可以用),无论是使用扩展变量作用域、基于接口的回调还是Handler-post/Handler-Message等方式,都可以直接处理此类问题,若适用广播机制,显然有些“杀鸡牛刀”的感觉,会显太“重”;
第二种情形:同一app内部的不同组件之间的消息通信(单个进程),对于此类需求,在有些教复杂的情况下单纯的依靠基于接口的回调等方式不好处理,此时可以直接使用EventBus等,相对而言,EventBus由于是针对统一进程,用于处理此类需求非常适合,且轻松解耦。可以参见文件《Android各组件/控件间通信利器之EventBus》。
第三、四、五情形:由于涉及不同进程间的消息通信,此时根据实际业务使用广播机制会显得非常适宜。下面主要针对Android广播中的具体知识点进行总结。
2.BroadcastReceiver注册类型
BroadcastReceiver总体上可以分为两种注册类型:静态注册和动态注册。
静态注册:
直接在AndroidManifest.xml文件中进行注册。规则如下:
<receiver android:enabled=["true" | "false"]android:exported=["true" | "false"]android:icon="drawable resource"android:label="string resource"android:name="string"android:permission="string"android:process="string" >. . .</receiver>
其中,需要注意的属性
android:exported 此broadcastReceiver能否接受其他App的发出的广播;
android:name 此broadcastReceiver类名;
android:permission 如果设置,具有相应权限的广播发送方发送的广播才能被此broadcastReceiver所接收;
android:process broadcastReceiver运行所处的进程。默认为app的进程。可以指定独立的进程(Android四大基本组件都可以通过此属性指定自己的独立进程)
常见的注册形式有:
<receiver android:name=".MyBroadcastReceiver" > <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter></receiver>
其中,intent-filter由于指定此广播接收器将用于接收特定的广播类型。本示例中给出的是用于接收网络状态改变或开启启动时系统自身所发出的广播。当此App首次启动时,系统会自动实例化MyBroadcastReceiver,并注册到系统中。
之前常说:静态注册的广播接收器即使app已经退出,主要有相应的广播发出,依然可以接收到。
但此种描述自Android 3.1开始将不再成立,Android官方文档在3.1文案中对此也进行了说明:http://developer.android.com/about/versions/android-3.1.html#launchcontrols。自Android3.1开始,系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,Android新增了两个Intent Flags,FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES,FLAG_INCLUDE_STOPPED_PACKAGES虽然作为默认值,但当系统发送广播时,直接增加了FLAG_EXCLUDE_STOPPED_PACKAGES,导致即使是静态注册的广播接收器,对于进程已经退出的app,同样无法接收到广播。
注:在3.1以前,相信不少app可能通过静态注册方式监听各种系统广播,以此进行一些业务上的处理(如即时app已经退出,仍然能接收到,可以启动service等..),3.1后,静态注册接受广播方式的改变,将直接导致此类方案不再可行。于是,通过将Service与App本身设置成不同的进程已经成为实现此类需求的可行替代方案。
动态注册:
动态注册时,无须在AndroidManifest中注册<receiver/>组件。直接在代码中通过调用Context的registerReceiver函数,可以在程序中动态注册BroadcastReceiver。registerReceiver的定义形式如下:
1 registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
2 registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)
典型的写法示例如下:
1 public class MainActivity extends Activity { 2 public static final String BROADCAST_ACTION = "com.example.corn"; 3 private BroadcastReceiver mBroadcastReceiver; 4 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 setContentView(R.layout.activity_main); 9 10 mBroadcastReceiver = new MyBroadcastReceiver();11 IntentFilter intentFilter = new IntentFilter();12 intentFilter.addAction(BROADCAST_ACTION);13 registerReceiver(mBroadcastReceiver, intentFilter);14 }15 16 @Override17 protected void onDestroy() {18 super.onDestroy();19 unregisterReceiver(mBroadcastReceiver);20 }21 22 }
注:Android中所有与观察者模式有关的设计中,一旦涉及到register,必定在相应的时机需要unregister。因此,上例在onDestroy()回到中需要unregisterReceiver(mBroadcastReceiver)。
当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中。
3.广播发送及广播类型
1)系统广播和应用广播(自定义广播)
Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开启启动,网络状态改变,拍照,屏幕关闭与开启,点亮不足等等。每个系统广播都具有特定的intent-filter,其中主要包括action,系统广播发出后,系统中当前的所有未结束的app进程只要注册了相应的BroadcastReceiver,都能接收到此系统广播。
自定义广播的定义过程,实际就是相应广播”意图“的定义过程,然后通过广播发送者将此”意图“发送出去。无论是系统广播还是自定义广播,相应的BroadcastReceiver接收后将会回调onReceive()函数。
下段代码片段显示的是一个自定义的广播并发送出去。其中setAction(..)对应于BroadcastReceiver中的intentFilter中的action。
1 Intent intent = new Intent();2 intent.setAction(BROADCAST_ACTION);3 intent.putExtra("name", "qqyumidi");4 sendBroadcast(intent);
下面演示的是同一app内部的同一组件内的消息通信(同一个线程 - UI线程,为方便起见,直接简单示例,正如前文分析所述,实际需求编码中,此类场景一般不可能用到广播机制)
1 public class MainActivity extends Activity { 2 public static final String BROADCAST_ACTION = "com.example.corn"; 3 private BroadcastReceiver mBroadcastReceiver; 4 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 setContentView(R.layout.activity_main); 9 10 mBroadcastReceiver = new MyBroadcastReceiver();11 IntentFilter intentFilter = new IntentFilter();12 intentFilter.addAction(BROADCAST_ACTION);13 registerReceiver(mBroadcastReceiver, intentFilter);14 }15 16 @Override17 protected void onResume() {18 super.onResume();19 new Handler().postDelayed(new Runnable() {20 @Override21 public void run() {22 Intent intent = new Intent();23 intent.setAction(BROADCAST_ACTION);24 intent.putExtra("name", "qqyumidi");25 sendBroadcast(intent);26 }27 }, 3 * 1000);28 29 }30 31 @Override32 protected void onDestroy() {33 super.onDestroy();34 unregisterReceiver(mBroadcastReceiver);35 }36 }37 38 public class MyBroadcastReceiver extends BroadcastReceiver {39 public static final String TAG = "MyBroadcastReceiver";40 41 @Override42 public void onReceive(Context context, Intent intent) {43 Log.w(TAG, "intent:" + intent);44 String name = intent.getStringExtra("name");45 Log.w(TAG, "name:" + name);46 }47 }
##输出结果为:
1 W/MyBroadcastReceiver(30362): intent:Intent { act=com.example.corn flg=0x10 (has extras) }2 W/MyBroadcastReceiver(30362): name:qqyumidi
2)
------- 未完待续。。
Android总结篇系列:Android广播机制