前言

Android四大组件重要性已经不言而喻了,今天谈谈的是Android中的广播机制。在我们上学的时候,每个班级的教室里都会装有一个喇叭,这些喇叭都是接入到学校的广播室的,一旦有什么重要的通知,就会播放一条广播来告知全校的师生。类似的工作机制其实在计算机领域也有很广泛的应用,如果你了解网络通信原理应该会知道,在一个 IP 网络范围中最大的 IP 地址是被保留作为广播地址来使用的。比如某个网络的 IP 范围是 192.168.0.XXX,子网掩码是 255.255.255.0,那么这个网络的广播地址就是 192.168.0.255。 广播数据包会被发送到同一网络上的所有端口,这样在该网络中的每台主机都将会收到这条广播。为了方便于进行系统级别的消息通知,Android 也引入了一套类似的广播消息机制。

目录

  • 广播机制介绍
  • BroadcastReceiver用法
  • 发送自定义广播
  • 本地广播介绍
  • 广播的注册过程
  • 广播的发送和接受过程
  • 总结

广播机制介绍

为什么说 Android中的广播机制更加灵活呢?这是因为 Android中的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序就只会接收到自己所关心的广播内容,这些 播可能是来自于系统的,也可能是来自于其他应用程序的。Android提供了一套完整的 API, 允许应用程序自由地发送和接收广播。接收广播的方法则需要引入一个新的概念,广播接收器(Broadcast Receiver),它就是用来接收来自系统和应用中的广播。

在Android广播中,主要分为两种类型:标准广播和有序广播。

标准广播(Normal broadcasts)是一种完全异步执行的广播,在广播发出之后,所有的 广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可 言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。标准广播的工作流程如图所示。

有序广播(Ordered broadcasts)则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。

BroadcastReceiver用法

BroadcastReceiver主要包括两方面的内容,一个是广播的注册过程,另一个是广播的发送和接收过程。那么该如何创建一个广播接收器呢?其实只需要新建一个类,让它继承自BroadcastReceiver, 并重写父类的 onReceive()方法就行了。这样当有广播到来时,onReceive()方法就会得到执行, 具体的逻辑就可以在这个方法中处理。 广播的使用方法有两个:静态方法和动态方法。

动态方法

  1. public class MainActivity extends Activity {
  2. private IntentFilter intentFilter;
  3. private NetworkChangeReceiver networkChangeReceiver;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.activity_main);
  8. intentFilter = new IntentFilter();
  9. intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
  10. networkChangeReceiver = new NetworkChangeReceiver();
  11. registerReceiver(networkChangeReceiver, intentFilter);
  12. }
  13. @Override
  14. protected void onDestroy() {
  15. super.onDestroy();
  16. unregisterReceiver(networkChangeReceiver);
  17. }
  18. private class NetworkChangeReceiver extends BroadcastReceiver {
  19. @Override
  20. public void onReceive(Context context, Intent intent) {
  21. Toast.makeText(context, "网络变化", Toast.LENGTH_SHORT).show();
  22. }
  23. }
  24. }

可以看到,我们在 MainActivity 中定义了一个内部类 NetworkChangeReceiver,这个类 是继承自 BroadcastReceiver的,并重写了父类的 onReceive()方法。这样每当网络状态发生变 化时,onReceive()方法就会得到执行,这里只是简单地使用 Toast提示了一段文本信息。

然后观察 onCreate()方法,首先我们创建了一个 IntentFilter 的实例,并给它添加了一个 值为 android.net.conn.CONNECTIVITY_CHANGE 的 action,为什么要添加这个值呢?因为 当网络状态发生变化时,系统发出的正是一条值为 android.net.conn.CONNECTIVITY_ CHANGE 的广播,也就是说我们的广播接收器想要监听什么广播,就在这里添加相应的 action就行了。接下来创建了一个 NetworkChangeReceiver的实例,然后调用 registerReceiver() 方法进行注册,将 NetworkChangeReceiver 的实例和 IntentFilter 的实例都传了进去,这样 NetworkChangeReceiver就会收到所有值为android.net.conn.CONNECTIVITY_CHANGE的广 播,也就实现了监听网络变化的功能。

最后要记得,动态注册的广播接收器一定都要取消注册才行,这里我们是在 onDestroy() 方法中通过调用 unregisterReceiver()方法来实现的。

静态方法

动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是 它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在 onCreate()方法中的。那么有没有什么办法可以让程序在未启动的情况下就能接收到广播 呢?这就需要使用静态注册的方式了。

这里我们准备让程序接收一条开机广播,当收到这条广播时就可以在 onReceive()方法里 执行相应的逻辑,从而实现开机启动的功能。新建一个 BootCompleteReceiver 继承自 BroadcastReceiver,代码如下所示

  1. public class BootCompleteReceiver extends BroadcastReceiver {
  2. @Override
  3. public void onReceive(Context context, Intent intent) {
  4. Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();
  5. }
  6. }

可以看到,这里不再使用内部类的方式来定义广播接收器,因为稍后我们需要在 AndroidManifest.xml中将这个广播接收器的类名注册进去。在 onReceive()方法中,还是简单 地使用 Toast弹出一段提示信息。

然后修改 AndroidManifest.xml文件,代码如下所示:

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2. package="com.example.broadcasttest"
  3. android:versionCode="1"
  4. android:versionName="1.0" >
  5. ……
  6. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  7. <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  8. <application
  9. android:allowBackup="true"
  10. android:icon="@drawable/ic_launcher"
  11. android:label="@string/app_name"
  12. android:theme="@style/AppTheme" >
  13. <receiver android:name=".BootCompleteReceiver" >
  14. <intent-filter>
  15. <action android:name="android.intent.action.BOOT_COMPLETED" />
  16. </intent-filter>
  17. </receiver>
  18. </application>
  19. </manifest>

标签内出现了一个新的标签,所有静态注册的广播接收器 都是在这里进行注册的。它的用法其实和标签非常相似,首先通过 android:name 来指定具体注册哪一个广播接收器,然后在标签里加入想要接收的广播就行了, 由于Android系统启动完成后会发出一条值为android.intent.action.BOOT_COMPLETED的广 播,因此我们在这里添加了相应的 action。

另外,监听系统开机广播也是需要声明权限的,可以看到,我们使用 标签又加入了一条 android.permission.RECEIVE_BOOT_COMPLETED权限

发送自定义广播

现在你已经学会了通过广播接收器来接收系统广播,接下来我们就要学习一下如何在应用程序中发送自定义的广播。前面已经介绍过了,广播主要分为两种类型,标准广播和有序 广播。

在API文档中关于BroadcastReceiver的概述:

  • 广播接收器是一个专注于接收广播通知信息,并做出对应处理的组件。很多广播是源自于系统代码的──比如,通知时区改变、电池电量低、拍摄了一张照片或者用户改变了语言选项。应用程序也可以进行广播──比如说,通知其它应用程序一些数据下载完成并处于可用状态。
  • 应用程序可以拥有任意数量的广播接收器以对所有它感兴趣的通知信息予以响应。所有的接收器均继承自BroadcastReceiver基类。
  • 广播接收器没有用户界面。然而,它们可以启动一个activity来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

那么广播事件的流程如何呢,如下:

  • 注册广播事件:注册方式有两种,一种是静态注册,就是在AndroidManifest.xml文件中定义,注册的广播接收器必须要继承BroadcastReceiver;另一种是动态注册,是在程序中使用Context.registerReceiver注册,注册的广播接收器相当于一个匿名类。两种方式都需要IntentFIlter。

  • 发送广播事件:通过Context.sendBroadcast来发送,由Intent来传递注册时用到的Action。

  • 接收广播事件:当发送的广播被接收器监听到后,会调用它的onReceive()方法,并将包含消息的Intent对象传给它。onReceive中代码的执行时间不要超过10s,否则Android会弹出超时dialog。

具体做法:

在发送广播之前,我们还是需要先定义一个广播接收器来准备接收此广播才行,不然发 出去也是白发。因此新建一个 MyBroadcastReceiver继承自 BroadcastReceiver

  1. public class MyBroadcastReceiver extends BroadcastReceiver {
  2. @Override
  3. public void onReceive(Context context, Intent intent) {
  4. Toast.makeText(context, "接收到广播消息", Toast.LENGTH_SHORT).show(); }
  5. }

这里当 MyBroadcastReceiver收到自定义的广播时,就会弹出提示语。然后在 AndroidManifest.xml中对这个广播接收器进行注册:

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2. package="com.example.broadcasttest"
  3. android:versionCode="1"
  4. android:versionName="1.0" >
  5. ……
  6. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  7. <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  8. <application
  9. android:allowBackup="true"
  10. android:icon="@drawable/ic_launcher"
  11. android:label="@string/app_name"
  12. android:theme="@style/AppTheme" >
  13. <receiver android:name=".MyBroadcastReceiver" >
  14. <intent-filter>
  15. <action android:name="com.example.broadcasttest.MY_BROADCAST" />
  16. </intent-filter>
  17. </receiver>
  18. </application>
  19. </manifest>

可以看到,这里让 MyBroadcastReceiver 接收一条值为 com.example.broadcasttest. MY_BROADCAST的广播,因此待会儿在发送广播的时候,我们就需要发出这样的一条广播。

  1. public class MainActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. Button button = (Button) findViewById(R.id.button);
  7. button.setOnClickListener(new OnClickListener() {
  8. @Override
  9. public void onClick(View v) {
  10. Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");
  11. sendBroadcast(intent);
  12. }
  13. });
  14. }
  15. }

可以看到,我们在按钮的点击事件里面加入了发送自定义广播的逻辑。首先构建出了一 个 Intent对象,并把要发送的广播的值传入,然后调用了 Context的 sendBroadcast()方法将广 播发送出去,这样所有监听 com.example.broadcasttest.MY_BROADCAST 这条广播的广播接 收器就会收到消息。此时发出去的广播就是一条标准广播。

本地广播介绍

前面我们发送和接收的广播全部都是属于系统全局广播,即发出的广播可以被其他任何的任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样就很容易会引起安全性的问题,比如说我们发送的一些携带关键性数据的广播有可能被其他的应用 程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。

为了能够简单地解决广播的安全性问题,Android 引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。 本地广播的用法并不复杂,主要就是使用了一个 LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。下面我们就通过具体的实例来尝试一下它的用法,修改 MainActivity中的代码,如下所示:

  1. public class MainActivity extends Activity {
  2. private IntentFilter intentFilter;
  3. private LocalReceiver localReceiver;
  4. private LocalBroadcastManager localBroadcastManager;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. localBroadcastManager = LocalBroadcastManager.getInstance(this);
  10. // 获取实例
  11. Button button = (Button) findViewById(R.id.button);
  12. button.setOnClickListener(new OnClickListener() {
  13. @Override
  14. public void onClick(View v) {
  15. Intent intent = new Intent("com.example.broadcasttest. LOCAL_BROADCAST");
  16. localBroadcastManager.sendBroadcast(intent);// 发送本地广播
  17. }
  18. });
  19. intentFilter = new IntentFilter();
  20. intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
  21. localReceiver = new LocalReceiver();
  22. // 注册本地广播监听器
  23. localBroadcastManager.registerReceiver(localReceiver, intentFilter);
  24. }
  25. @Override
  26. protected void onDestroy() {
  27. super.onDestroy();
  28. localBroadcastManager.unregisterReceiver(localReceiver);
  29. }
  30. private class LocalReceiver extends BroadcastReceiver {
  31. @Override
  32. public void onReceive(Context context, Intent intent) {
  33. Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show();
  34. }
  35. }
  36. }

有没有感觉这些代码很熟悉?没错,其实这基本上就和我们前面所学的动态注册广播接 收器以及发送广播的代码是一样。只不过现在首先是通过 LocalBroadcastManager的 getInstance() 方法得到了它的一个实例,然后在注册广播接收器的时候调用的是 LocalBroadcastManager 的 registerReceiver()方法,在发送广播的时候调用的是 LocalBroadcastManager的 sendBroadcast() 方法,仅此而已。

另外还有一点需要说明,本地广播是无法通过静态注册的方式来接收的。其实这也完全 可以理解,因为静态注册主要就是为了让程序在未启动的情况下也能收到广播,而发送本地 广播时,我们的程序肯定是已经启动了,因此也完全不需要使用静态注册的功能。

总结下使用本地广播的几点优势吧。

  1. 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄 漏的问题。
  2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐 患。
  3. 发送本地广播比起发送系统全局广播将会更加高效。

广播的注册过程

我们现在知道了广播的注册有静态注册和动态注册,其中静态注册的广播在应用安装时由系统自动完成注册的。具体来说是由PMS(PackageManagerService)来完成整个注册过程的,除了广播以为,其他三大组件也都是应用安装时由PMS解析并注册的,这里分析下广播的动态注册过程,动态注册过程是从ContextWrapper的registerReceiver方法开始的,和Activity以及Service一样。ContextWrapper并没有做实际的工作,基本将注册过程直接交给ContextImpl来完成。

  1. @Override
  2. public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
  3. return registerReceiver(receiver, filter, null, null);
  4. }

ContextImpl的registerReceiver方法调用了自己的registerReceiverInternal方法,具体实现如下:

  1. private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
  2. IntentFilter filter, String broadcastPermission,Handler scheduler, Context context) {
  3. IIntentReceiver rd = null;
  4. if (receiver != null) {
  5. if (mPackageInfo != null && context != null) {
  6. if (scheduler == null) {
  7. scheduler = mMainThread.getHandler();
  8. }
  9. rd = mPackageInfo.getReceiverDispatcher(
  10. receiver, context, scheduler,
  11. mMainThread.getInstrumentation(), true);
  12. } else {
  13. if (scheduler == null) {
  14. scheduler = mMainThread.getHandler();
  15. }
  16. rd = new LoadedApk.ReceiverDispatcher(
  17. receiver, context, scheduler, null, true).getIIntentReceiver();
  18. }
  19. }
  20. try {
  21. return ActivityManagerNative.getDefault().registerReceiver(
  22. mMainThread.getApplicationThread(), mBasePackageName,
  23. rd, filter, broadcastPermission, userId);
  24. } catch (RemoteException e) {
  25. return null;
  26. }
  27. }

系统首先从mPackageInfo获取IIntentReceiver对象,然后再采用跨进程的方式向AMS发送广播注册的请求。之所以用IIntentReceiver而不是直接采用BroadcastReceiver,这是因为上述注册过程是一个进程间通信的过程,而BroadcastReceiver作为一个Android组件是不能直接跨进程传递的,所以需要通过IIntentReceiver来中转一下,毫无疑问,IIntentReceiver必须是一个Binder接口,它的具体实现是LoadedApk.ReceiverDispatcher,ReceiverDispatcher的内部同时保存了BroadcastReceiver和InnerReceiver,这样当接收到广播时ReceiverDispatcher可以很方便地调用BroadcastReceiver的onReceiver方法。

这里的ActivityManagerNative.getDefault()实际上就是一个AMS。具体代码如下:

  1. public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,Context context, Handler handler,Instrumentation instrumentation, boolean registered) {
  2. synchronized (mReceivers) {
  3. LoadedApk.ReceiverDispatcher rd = null;
  4. ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
  5. if (registered) {
  6. map = mReceivers.get(context);
  7. if (map != null) {
  8. rd = map.get(r);
  9. }
  10. }
  11. if (rd == null) {
  12. rd = new ReceiverDispatcher(r, context, handler,instrumentation, registered);
  13. if (registered) {
  14. if (map == null) {
  15. map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
  16. mReceivers.put(context, map);
  17. }
  18. map.put(r, rd);
  19. }
  20. } else {
  21. rd.validate(context, handler);
  22. }
  23. rd.mForgotten = false;
  24. return rd.getIIntentReceiver();
  25. }

由于注册的广播真正的实现过程是在AMS中,最终会把远程的InnerReceiver对象以及IntentFilter对象存储起来,这样整个广播的注册过程就完成了。

广播的发送和接受过程

当通过send方法来发送广播时,AMS会查找出匹配的广播接收者并将广播发送给他们处理。广播的发送有几种类型:普通广播,有序广播和粘性广播。这里分析下普通广播的实现。

广播的发送和接收。其本质是一个过程的两个阶段。广播的发送仍然开始于ContextWrapper的sendBroadcast方法,之所以不是Context,那是因为Context的sendBroadcast是一个抽象方法。和广播的注册过程一样ContextWrapper的sendBroadcast方法仍然什么都不做,只是把事情交给ContextImpl去处理,ContextImpl的sendBroadcast方法源码如下:

  1. public void sendBroadcast(Intent intent) {
  2. warnIfCallingFromSystemProcess();
  3. String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
  4. try {
  5. intent.prepareToLeaveProcess();
  6. ActivityManagerNative.getDefault().broadcastIntent(
  7. mMainThread.getApplicationThread(), intent, resolvedType, null,
  8. Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,
  9. getUserId());
  10. } catch (RemoteException e) {
  11. }
  12. }

它直接向AMS发起了一个异步请求用于发送广播。那么AMS的broadcastIntent方法的源码如下:

  1. public final int broadcastIntent(IApplicationThread caller,
  2. Intent intent, String resolvedType, IIntentReceiver resultTo,
  3. int resultCode, String resultData, Bundle map,
  4. String requiredPermission, boolean serialized, boolean sticky, int userId) {
  5. synchronized(this) {
  6. intent = verifyBroadcastLocked(intent);
  7. final ProcessRecord callerApp = getRecordForAppLocked(caller);
  8. final int callingPid = Binder.getCallingPid();
  9. final int callingUid = Binder.getCallingUid();
  10. final long origId = Binder.clearCallingIdentity();
  11. int res = broadcastIntentLocked(callerApp,
  12. callerApp != null ? callerApp.info.packageName : null,
  13. intent, resolvedType, resultTo,
  14. resultCode, resultData, map, requiredPermission, serialized, sticky,
  15. callingPid, callingUid, userId);
  16. Binder.restoreCallingIdentity(origId);
  17. return res;
  18. }
  19. }

从代码上看,broadcastIntent调用了broadcastIntentLocked方法,但在AMS的broadcastIntentLocked方法里有这么一句:

  1. // By default broadcasts do not go to stopped apps.
  2. intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

这表示在Android5.0下,默认情况下广播不会发送给已经停止的应用。FLAG_EXCLUDE_STOPPED_PACKAGES的含义是表示 不包含已经停止的应用,这个时候广播不会发送给已经停止的应用。

在broadcastIntentLocked的内部,会根据intent-filter查找出匹配的广播接收者并经过一系列的条件过滤,最终会将满足条件的广播接收者添加到BroadcastQueue中,接着BroadcastQueue将会广播发送给相应的广播接收者。

  1. if ((receivers != null && receivers.size() > 0)
  2. || resultTo != null) {
  3. BroadcastQueue queue = broadcastQueueForIntent(intent);
  4. BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
  5. callerPackage, callingPid, callingUid, requiredPermission,
  6. receivers, resultTo, resultCode, resultData, map, ordered,
  7. sticky, false);
  8. if (DEBUG_BROADCAST) Slog.v(
  9. TAG, "Enqueueing ordered broadcast " + r
  10. + ": prev had " + queue.mOrderedBroadcasts.size());
  11. if (DEBUG_BROADCAST) {
  12. int seq = r.intent.getIntExtra("seq", -1);
  13. Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq);
  14. }
  15. boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
  16. if (!replaced) {
  17. queue.enqueueOrderedBroadcastLocked(r);
  18. queue.scheduleBroadcastsLocked();
  19. }
  20. }

下面看下BroadcastQueue中广播的发送过程的实现。如下所示:

  1. public void scheduleBroadcastsLocked() {
  2. if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts ["
  3. + mQueueName + "]: current="
  4. + mBroadcastsScheduled);
  5. if (mBroadcastsScheduled) {
  6. return;
  7. }
  8. mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
  9. mBroadcastsScheduled = true;
  10. }

BroadcastQueue的scheduleBroadcastsLocked方法并没有立即发送广播,而是发送了一个BROADCAST_INTENT_MSG类型的消息,BroadcastQueue收到消息后会调用processNextBroadcast方法,BroadcastQueue的processNextBroadcast方法对普通广播的处理方式如下:

  1. // First, deliver any non-serialized broadcasts right away.
  2. while (mParallelBroadcasts.size() > 0) {
  3. r = mParallelBroadcasts.remove(0);
  4. r.dispatchTime = SystemClock.uptimeMillis();
  5. r.dispatchClockTime = System.currentTimeMillis();
  6. final int N = r.receivers.size();
  7. if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast ["
  8. + mQueueName + "] " + r);
  9. for (int i=0; i<N; i++) {
  10. Object target = r.receivers.get(i);
  11. if (DEBUG_BROADCAST) Slog.v(TAG,
  12. "Delivering non-ordered on [" + mQueueName + "] to registered "
  13. + target + ": " + r);
  14. deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
  15. }
  16. addBroadcastToHistoryLocked(r);
  17. if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast ["
  18. + mQueueName + "] " + r);
  19. }

可以看到,无序广播存储在mParallelBroadcasts中,系统会遍历mParallelBroadcasts并将其中的广播发送给它们所有接收者,具体的发送过程是通过deliverToRegisteredReceiverLocked方法来实现的。

最终呢,会调用ApplicationThread的scheduleRegisteredReceiver的实现比较简单,它通过InnerReceiver来实现广播的接收。然后InnerReceiver的performReceive方法会调用LoadedApk.ReceiverDispatcher的PerformReceive方法。最终会回调到receiver.onReceive()这个方法。

很显然,这个时候BroadcastReceiver的onReceive方法被执行了,也就是说应用收到广播了,同时,onReceive方法是在广播接收者的主线程中被调用,所以不能做耗时操作,因为是在ApplicationThread的主线程上执行的。

总结

总结一下,Android中应用程序发送广播的过程:

  • 通过sendBroadcast把一个广播通过Binder发送给ActivityManagerService,ActivityManagerService根据这个广播的Action类型找到相应的广播接收器,然后把这个广播放进自己的消息队列中,就完成第一阶段对这个广播的异步分发。
  • ActivityManagerService在消息循环中处理这个广播,并通过Binder机制把这个广播分发给注册的ReceiverDispatcher,ReceiverDispatcher把这个广播放进MainActivity所在线程的消息队列中,就完成第二阶段对这个广播的异步分发。
  • ReceiverDispatcher的内部类Args在MainActivity所在的线程消息循环中处理这个广播,最终是将这个广播分发给所注册的BroadcastReceiver实例的onReceive函数进行处理:

作为Android中四大组件之一的广播,可以应用很多场景的,比如用户异地登陆强制下线,应用开机启动服务,网络状态变化通知等等,掌握好其中的定义,使用方法,背后的注册流程,发送和接收消息流程机制,对于我们在开发时是很有帮助的。

参考信息:

1,http://blog.csdn.net/zuolongsnail/article/details/6450156

2,《第一行代码》

阅读扩展

源于对掌握的Android开发基础点进行整理,罗列下已经总结的文章,从中可以看到技术积累的过程。

1,Android系统简介

2,ProGuard代码混淆

3,讲讲Handler+Looper+MessageQueue关系

4,Android图片加载库理解

5,谈谈Android运行时权限理解

6,EventBus初理解

7,Android 常见工具类

8,对于Fragment的一些理解

9,Android 四大组件之 " Activity "

10,Android 四大组件之" Service "

11,Android 四大组件之“ BroadcastReceiver "

12,Android 四大组件之" ContentProvider "

13,讲讲 Android 事件拦截机制

14,Android 动画的理解

15,Android 生命周期和启动模式

16,Android IPC 机制

17,View 的事件体系

18,View 的工作原理

19,理解 Window 和 WindowManager

20,Activity 启动过程分析

21,Service 启动过程分析

22,Android 性能优化

23,Android 消息机制

24,Android Bitmap相关

25,Android 线程和线程池

26,Android 中的 Drawable 和动画

27,RecylerView 中的装饰者模式

28,Android 触摸事件机制

29,Android 事件机制应用

30,Cordova 框架的一些理解

31,有关 Android 插件化思考

32,开发人员必备技能——单元测试

Android 四大组件之“ BroadcastReceiver ”的更多相关文章

  1. [置顶] Android四大组件之BroadcastReceiver

    Android四大组件之BroadcastReceiver Broadcast Receiver 广播接收器,是一种负责接收广播消息并对消息做出响应的组件,和Service一样并不提供与用户交互的UI ...

  2. Android四大组件:BroadcastReceiver 介绍

    介绍 BroadcastReceiver 即广播组件,是 Android 的四大组件之一.用于监听和接收广播消息,并做出响应.有以下一些应用: 不同组件之间的通信(应用内或不同应用之间). 多线程之间 ...

  3. Android四大组件之BroadcastReceiver

    什么是BroadcastReceiver? BroadcastReceiver也就是“广播接收者”的意思,顾名思义,它就是用来接收来自系统和应用中的广播. 在Android系统中,广播体现在方方面面, ...

  4. Android四大组件之—— BroadcastReceiver的使用

    BroadcastReceiver又名广播接收者.既然它用于接收广播,那一定就有人负责发送. Android系统中的广播: 在现实生活中,我们都知道广播是什么,用来做什么.例如公园里的广播,主要通知游 ...

  5. Android 四大组件之 BroadcastReceiver

    0  简介        BroadcastReceiver也就是“广播接收者”的意思,顾名思义,它就是用来接收来自系统和应用中的 广播.        在Android系统中,广播体现在方方面面,例 ...

  6. Android四大组件之一“广播”

    前言 Android四大组件重要性已经不言而喻了,今天谈谈的是Android中的广播机制.在我们上学的时候,每个班级的教室里都会装有一个喇叭,这些喇叭都是接入到学校的广播室的,一旦有什么重要的通知,就 ...

  7. Android 四大组件之" ContentProvider "

    前言 ContentProvider作为Android的四大组件之一,是属于需要掌握的基础知识,可能在我们的应用中,对于Activity和Service这两个组件用的很常见,了解的也很多,但是对Con ...

  8. 【Android开发日记】之入门篇(六)——Android四大组件之Broadcast Receiver

    广播接受者是作为系统的监听者存在着的,它可以监听系统或系统中其他应用发生的事件来做出响应.如设备开机时,应用要检查数据的变化状况,此时就可以通过广播来把消息通知给用户.又如网络状态改变时,电量变化时都 ...

  9. Android实训案例(六)——四大组件之一BroadcastReceiver的基本使用,拨号,短信,SD卡,开机,应用安装卸载监听

    Android实训案例(六)--四大组件之一BroadcastReceiver的基本使用,拨号,短信,SD卡,开机,应用安装卸载监听 Android中四大组件的使用时重中之重,我这个阶段也不奢望能把他 ...

随机推荐

  1. Armadillo installation

    1.dependencies sudo apt-get install libopenblas-devsudo apt-get install liblapack-devsudo apt-get in ...

  2. 安装ubuntu16.04的时候出现的detecting file system

    解决问题方法是,进入主界面执行,如下操作即可: sudo umount -l /isodevice

  3. 【Java】Maven Tomcat插件使用

    本例是用的是tomcat7-maven-plugin 插件 依赖 tomcat7-maven-plugin 插件的pom.xml依赖为 <dependency> <groupId&g ...

  4. [html]Sublime Text添加插件

    今天想在Sublime Text(简称ST)内编写HTML后直接使用浏览器看效果,想添加View in Browser插件,然后遇到奇怪的问题添加插件直接报"找不到有用的插件" 一 ...

  5. ManageEngine OMP帮助台委派

  6. kbmmw 中XML 操作入门

    delphi 很早以前就自带了xml 的操作,最新版里面有三种XML 解释器,一种是MSXML,看名字就知道 这个是微软自带的,这个据delphi 官方称是速度是最快的,但是只能在windows 上使 ...

  7. Ajax的爬取心得

    一.查找到js的网址 在我们做爬虫的时候,如何判断一个数据是Ajax(asynchronous JavaScript And Xml,异步的JavaScript和Xml), 首先是数据的加载,在请求网 ...

  8. 42.OC中instancetype与id的区别

    区别: 在ARC(Auto Reference Count)环境下: instancetype用来在编译期确定实例的类型,而使用id的话,编译器不检查类型,运行时检查类型 在MRC(Manual Re ...

  9. PKG_CONFIG_PATH变量 与 ld.so.conf 文件

    一.编译和连接 一般来说,如果库的头文件不在 /usr/include 目录中,那么在编译的时候需要用 -I 参数指定其路径.由于同一个库在不同系统上可能位于不同的目录下,用户安装库的时候也可以将库安 ...

  10. python coroutine的学习跟总结[转]

    简介 因为最近一段时间需要研究一些openstack相关的东西,在阅读一些相关代码的时候碰到很多python特定的一些特性,比如generator, coroutine以及一些相关的类库,比如even ...