说明:本文是基于Android6.0源码来分析的

概要

  1. 我的理解是,Android中的广播可以看为一种消息机制,用来在一定的条件下触发一些操纵,比如:网络状态的改变,熄屏,亮屏等等Android系统都是通过广播来通知我们的。
  2. 广播的既可以在一个进程内通信,也可以跨进程进行通信,所以也是Android中IPC的一种方式。
  3. 一般我们是在Activity或者service中注册广播的

  • Android中的广播分两种
  1. 静态注册的广播:就是通过在Mainfest.xml文件中通过broadcast的tag来注册一个广播的
  2. 动态注册的广播:通过ContextImpl的registerReceiver来动态的注册一个广播,可在合适的时候调用unregisterReceiver来取消注册的广播。

  • 接下俩我们就从源码的角度来分析和梳理一下广播的注册过程。
  1. 首先我们从ContextImpl的registerReceiver方法开始。
@Overridepublic Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {return registerReceiver(receiver, filter, null, null);}@Overridepublic Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,String broadcastPermission, Handler scheduler) {return registerReceiverInternal(receiver, getUserId(),filter, broadcastPermission, scheduler, getOuterContext());}

上面只是调用一些重载的方法,载registerReceiverInternal有一些逻辑判断,我们可以分析一下

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,IntentFilter filter, String broadcastPermission,Handler scheduler, Context context) {IIntentReceiver rd = null;if (receiver != null) {if (mPackageInfo != null && context != null) {//scheduler传进来是null,所以会进来,scheduler其实就是注册广播时候所在的进程的handler,这里看到就是主线程的handler。if (scheduler == null) {scheduler = mMainThread.getHandler();}rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,mMainThread.getInstrumentation(), true);} else {if (scheduler == null) {scheduler = mMainThread.getHandler();}//这个地方很重要,多种会调用到他的一个成员变量里。rd = new LoadedApk.ReceiverDispatcher(receiver, context, scheduler, null, true).getIIntentReceiver();}}try {return ActivityManagerNative.getDefault().registerReceiver(mMainThread.getApplicationThread(), mBasePackageName,rd, filter, broadcastPermission, userId);} catch (RemoteException e) {return null;}}

我们可以分析一下getReceiverDispatcher方法。

 public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,Context context, Handler handler,Instrumentation instrumentation, boolean registered) {synchronized (mReceivers) {LoadedApk.ReceiverDispatcher rd = null;ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;//传进来的是true,所以会进来if (registered) {map = mReceivers.get(context);if (map != null) {rd = map.get(r);}}//没有找到广播分发者,我们就创建一个并添加到map, 再把map放到mReceivers中if (rd == null) {rd = new ReceiverDispatcher(r, context, handler,instrumentation, registered);if (registered) {if (map == null) {map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();mReceivers.put(context, map);}map.put(r, rd);}} else {//检验context和handle是不是注册广播时候的context和handlerd.validate(context, handler);}rd.mForgotten = false;return rd.getIIntentReceiver();}}

下面是mReceivers的定义。

 private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers= new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

mReceivers存放着当前进程的所有广播,key是Context,value是一个ArrayMap,可以看出,一个Context对应着多个广播。
记者调用来AMS的registerReceiver

public Intent registerReceiver(IApplicationThread caller, String callerPackage,IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {enforceNotIsolatedCaller("registerReceiver");ArrayList<Intent> stickyIntents = null;ProcessRecord callerApp = null;int callingUid;int callingPid;synchronized(this) {if (caller != null) {//找到注册广播所在的进程callerApp = getRecordForAppLocked(caller);if (callerApp == null) {throw new SecurityException("Unable to find app for caller " + caller+ " (pid=" + Binder.getCallingPid()+ ") when registering receiver " + receiver);}if (callerApp.info.uid != Process.SYSTEM_UID &&!callerApp.pkgList.containsKey(callerPackage) &&!"android".equals(callerPackage)) {throw new SecurityException("Given caller package " + callerPackage+ " is not running in process " + callerApp);}callingUid = callerApp.info.uid;callingPid = callerApp.pid;} else {callerPackage = null;callingUid = Binder.getCallingUid();callingPid = Binder.getCallingPid();}userId = handleIncomingUser(callingPid, callingUid, userId,true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);//这段逻辑是黏性广播的处理Iterator<String> actions = filter.actionsIterator();if (actions == null) {ArrayList<String> noAction = new ArrayList<String>(1);noAction.add(null);actions = noAction.iterator();}// Collect stickies of usersint[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };while (actions.hasNext()) {String action = actions.next();for (int id : userIds) {ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);if (stickies != null) {ArrayList<Intent> intents = stickies.get(action);if (intents != null) {if (stickyIntents == null) {stickyIntents = new ArrayList<Intent>();}stickyIntents.addAll(intents);}}}}}ArrayList<Intent> allSticky = null;if (stickyIntents != null) {final ContentResolver resolver = mContext.getContentResolver();// Look for any matching sticky broadcasts...for (int i = 0, N = stickyIntents.size(); i < N; i++) {Intent intent = stickyIntents.get(i);if (filter.match(resolver, intent, true, TAG) >= 0) {if (allSticky == null) {allSticky = new ArrayList<Intent>();}allSticky.add(intent);}}}// The first sticky in the list is returned directly back to the client.Intent sticky = allSticky != null ? allSticky.get(0) : null;if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);if (receiver == null) {return sticky;}synchronized (this) {if (callerApp != null && (callerApp.thread == null|| callerApp.thread.asBinder() != caller.asBinder())) {// Original caller already diedreturn null;}ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());if (rl == null) {//新的广播如果未注册,创建接受列表rl = new ReceiverList(this, callerApp, callingPid, callingUid,userId, receiver);if (rl.app != null) {rl.app.receivers.add(rl);} else {try {receiver.asBinder().linkToDeath(rl, 0);} catch (RemoteException e) {return sticky;}rl.linkedToDeath = true;}//把我们要注册的广播存放到mRegisteredReceivers中mRegisteredReceivers.put(receiver.asBinder(), rl);} else if (rl.uid != callingUid) {throw new IllegalArgumentException("Receiver requested to register for uid " + callingUid+ " was previously registered for uid " + rl.uid);} else if (rl.pid != callingPid) {throw new IllegalArgumentException("Receiver requested to register for pid " + callingPid+ " was previously registered for pid " + rl.pid);} else if (rl.userId != userId) {throw new IllegalArgumentException("Receiver requested to register for user " + userId+ " was previously registered for user " + rl.userId);}BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,permission, callingUid, userId);rl.add(bf);if (!bf.debugCheck()) {Slog.w(TAG, "==> For Dynamic broadcast");}//把新创建的广播过滤者加到mReceiverResolvermReceiverResolver.addFilter(bf);// Enqueue broadcasts for all existing stickies that match// this filter.if (allSticky != null) {ArrayList receivers = new ArrayList();receivers.add(bf);final int stickyCount = allSticky.size();for (int i = 0; i < stickyCount; i++) {Intent intent = allSticky.get(i);//根据intent返回前台或者后台广播BroadcastQueue queue = broadcastQueueForIntent(intent);BroadcastRecord r = new BroadcastRecord(queue, intent, null,null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,null, 0, null, null, false, true, true, -1);//把新建的广播记录加到队列中。queue.enqueueParallelBroadcastLocked(r);//开始下一个广播queue.scheduleBroadcastsLocked();}}return sticky;}}

广播的注册就结束来,下面我们总结一下。

  1. registerBorafcast传递的参数为广播接收者BroadcastReceiver和Intent的过滤条件IntentFilter的action的过滤条件。
  2. 创建LoadedApk.ReceiverDispatcher.InnerReceiver对象。
  3. 跳转到AMS进程,当广播Boradreceiver还没有注册过,则创建广播接收者队列ReceiverList,并添加到AMS.mRegisteredReceivers。
  4. 创建BroadcastFilter,并添加到AMS.mReceiverResolver。
  5. 如果我们的广播是粘性官博,则创建广播记录BroadcastRecord,并添加到BroadcastQueue广播队列中
  • 接下来我们分析广播的发送流程
    首先从sendBoradcast开始
 @Overridepublic void sendBroadcast(Intent intent) {warnIfCallingFromSystemProcess();String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());try {intent.prepareToLeaveProcess();ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null,Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,getUserId());} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}}

直接调用来AMS的broadcastIntent

public final int broadcastIntent(IApplicationThread caller,Intent intent, String resolvedType, IIntentReceiver resultTo,int resultCode, String resultData, Bundle resultExtras,String[] requiredPermissions, int appOp, Bundle options,boolean serialized, boolean sticky, int userId) {enforceNotIsolatedCaller("broadcastIntent");synchronized(this) {intent = verifyBroadcastLocked(intent);
//找到发送广播所在的进程final ProcessRecord callerApp = getRecordForAppLocked(caller);final int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();int res = broadcastIntentLocked(callerApp,callerApp != null ? callerApp.info.packageName : null,intent, resolvedType, resultTo, resultCode, resultData, resultExtras,requiredPermissions, appOp, null, serialized, sticky,callingPid, callingUid, userId);Binder.restoreCallingIdentity(origId);return res;}}
       private final int broadcastIntentLocked(ProcessRecord callerApp,String callerPackage, Intent intent, String resolvedType,IIntentReceiver resultTo, int resultCode, String resultData,Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle options,boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {intent = new Intent(intent);//广播不会发送给已经停掉的appintent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);//如果重启还没完成,不允熙开启新进程.if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);}...BroadcastOptions brOptions = null;...int callingAppId = UserHandle.getAppId(callingUid);if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID|| callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID|| callingAppId == Process.NFC_UID || callingUid == 0) {// Always okay.} else if (callerApp == null || !callerApp.persistent) {try {//如果是系统广播,抛出安全异常if (AppGlobals.getPackageManager().isProtectedBroadcast(intent.getAction()) && !AppGlobals.getPackageManager().isProtectedBroadcastAllowed(intent.getAction(), callingUid)) {String msg = "Permission Denial: not allowed to send broadcast "+ intent.getAction() + " from pid="+ callingPid + ", uid=" + callingUid;Slog.w(TAG, msg);throw new SecurityException(msg);} else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(intent.getAction())) {}final String action = intent.getAction();if (action != null) {switch (action) {case Intent.ACTION_UID_REMOVED://移除uidcase Intent.ACTION_PACKAGE_REMOVED://移除packagecase Intent.ACTION_PACKAGE_CHANGED://package改变case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE://外部设备不可用case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE://外部设备可用switch (action) {case Intent.ACTION_UID_REMOVED:final Bundle intentExtras = intent.getExtras();final int uid = intentExtras != null? intentExtras.getInt(Intent.EXTRA_UID) : -1;if (uid >= 0) {mBatteryStatsService.removeUid(uid);mAppOpsService.uidRemoved(uid);}break;case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:// If resources are unavailable just force stop all those packages// and flush the attribute cache as well.String list[] =intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);if (list != null && list.length > 0) {for (int i = 0; i < list.length; i++) {forceStopPackageLocked(list[i], -1, false, true, true,false, false, userId, "storage unmount");}mRecentTasks.cleanupLocked(UserHandle.USER_ALL);sendPackageBroadcastLocked(IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list,userId);}break;case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:mRecentTasks.cleanupLocked(UserHandle.USER_ALL);break;case Intent.ACTION_PACKAGE_REMOVED:case Intent.ACTION_PACKAGE_CHANGED:Uri data = intent.getData();String ssp;if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action);boolean fullUninstall = removed &&!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);final boolean killProcess =!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false);if (killProcess) {forceStopPackageLocked(ssp, UserHandle.getAppId(intent.getIntExtra(Intent.EXTRA_UID, -1)),false, true, true, false, fullUninstall, userId,removed ? "pkg removed" : "pkg changed");}if (removed) {sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,new String[] {ssp}, userId);if (fullUninstall) {mAppOpsService.packageRemoved(intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);// Remove all permissions granted from/to this packageremoveUriPermissionsForPackageLocked(ssp, userId, true);removeTasksByPackageNameLocked(ssp, userId);mBatteryStatsService.notePackageUninstalled(ssp);}} else {cleanupDisabledPackageComponentsLocked(ssp, userId, killProcess,intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST));}}break;}break;case Intent.ACTION_PACKAGE_ADDED:// Special case for adding a package: by default turn on compatibility mode.Uri data = intent.getData();String ssp;if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {final boolean replacing =intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);mCompatModePackages.handlePackageAddedLocked(ssp, replacing);try {ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(ssp, 0, 0);mBatteryStatsService.notePackageInstalled(ssp,ai != null ? ai.versionCode : 0);} catch (RemoteException e) {}}break;case Intent.ACTION_TIMEZONE_CHANGED:// If this is the time zone changed action, queue up a message that will reset// the timezone of all currently running processes. This message will get// queued up before the broadcast happens.mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);break;case Intent.ACTION_TIME_CHANGED:// If the user set the time, let all running processes know.final int is24Hour =intent.getBooleanExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, false) ? 1: 0;mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0));BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();synchronized (stats) {stats.noteCurrentTimeChangedLocked();}break;case Intent.ACTION_CLEAR_DNS_CACHE:mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);break;case Proxy.PROXY_CHANGE_ACTION:ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));break;case cyanogenmod.content.Intent.ACTION_PROTECTED_CHANGED:final boolean state =intent.getBooleanExtra(cyanogenmod.content.Intent.EXTRA_PROTECTED_STATE, false);if (state == PackageManager.COMPONENT_PROTECTED_STATUS) {cleanupProtectedComponentTasksLocked();}break;}}// 如果有sticky类型的广播,处理sticky类型的广播,一般情况下,传进来的sticky是false  if (sticky) {if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,callingPid, callingUid)!= PackageManager.PERMISSION_GRANTED) {String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="+ callingPid + ", uid=" + callingUid+ " requires " + android.Manifest.permission.BROADCAST_STICKY;Slog.w(TAG, msg);throw new SecurityException(msg);}if (requiredPermissions != null && requiredPermissions.length > 0) {Slog.w(TAG, "Can't broadcast sticky intent " + intent+ " and enforce permissions " + Arrays.toString(requiredPermissions));return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;}if (intent.getComponent() != null) {throw new SecurityException("Sticky broadcasts can't target a specific component");}// We use userId directly here, since the "all" target is maintained// as a separate set of sticky broadcasts.if (userId != UserHandle.USER_ALL) {// But first, if this is not a broadcast to all users, then// make sure it doesn't conflict with an existing broadcast to// all users.ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(UserHandle.USER_ALL);if (stickies != null) {ArrayList<Intent> list = stickies.get(intent.getAction());if (list != null) {int N = list.size();int i;for (i=0; i<N; i++) {if (intent.filterEquals(list.get(i))) {throw new IllegalArgumentException("Sticky broadcast " + intent + " for user "+ userId + " conflicts with existing global broadcast");}}}}}//把没注册的sticky广播注册到mStickyBroadcasts中ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);if (stickies == null) {stickies = new ArrayMap<>();mStickyBroadcasts.put(userId, stickies);}ArrayList<Intent> list = stickies.get(intent.getAction());if (list == null) {list = new ArrayList<>();stickies.put(intent.getAction(), list);}final int stickiesCount = list.size();int i;for (i = 0; i < stickiesCount; i++) {if (intent.filterEquals(list.get(i))) {//发送的sticky广播如果已经存在了,则用最新的替换已经高存在的.list.set(i, new Intent(intent));break;}}if (i >= stickiesCount) {//添加新的广播list.add(new Intent(intent));}}int[] users;if (userId == UserHandle.USER_ALL) {// Caller wants broadcast to go to all started users.users = mStartedUserArray;} else {// Caller wants broadcast to go to one specific user.users = new int[] {userId};}// Figure out who all will receive this broadcast.List receivers = null;List<BroadcastFilter> registeredReceivers = null;// Need to resolve the intent to interested receivers...if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)== 0) {//收集静态广播receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);}这里不为nullif (intent.getComponent() == null) {if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {// Query one target user at a time, excluding shell-restricted usersUserManagerService ums = getUserManagerLocked();for (int i = 0; i < users.length; i++) {if (ums.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {continue;}List<BroadcastFilter> registeredReceiversForUser =mReceiverResolver.queryIntent(intent,resolvedType, false, users[i]);if (registeredReceivers == null) {registeredReceivers = registeredReceiversForUser;} else if (registeredReceiversForUser != null) {registeredReceivers.addAll(registeredReceiversForUser);}}} else {//收集动态注册的广播//mReceiverResolver里面装这已经注册过的resolverregisteredReceivers = mReceiverResolver.queryIntent(intent,resolvedType, false, userId);}}final boolean replacePending =(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueing broadcast: " + intent.getAction()+ " replacePending=" + replacePending);int NR = registeredReceivers != null ? registeredReceivers.size() : 0;if (!ordered && NR > 0) {// If we are not serializing this broadcast, then send the// registered receivers separately so they don't wait for the// components to be launched.final BroadcastQueue queue = broadcastQueueForIntent(intent);BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,resultExtras, ordered, sticky, false, userId);if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);if (!replaced) {queue.enqueueParallelBroadcastLocked(r);queue.scheduleBroadcastsLocked();}registeredReceivers = null;NR = 0;}// Merge into one list.int ir = 0;if (receivers != null) {...if (skipPackages != null && (skipPackages.length > 0)) {for (String skipPackage : skipPackages) {if (skipPackage != null) {int NT = receivers.size();for (int it=0; it<NT; it++) {ResolveInfo curt = (ResolveInfo)receivers.get(it);if (curt.activityInfo.packageName.equals(skipPackage)) {receivers.remove(it);it--;NT--;}}}}}int NT = receivers != null ? receivers.size() : 0;int it = 0;ResolveInfo curt = null;BroadcastFilter curr = null;//把动态注册的广播和静态注册的广播合并成一个while (it < NT && ir < NR) {if (curt == null) {curt = (ResolveInfo)receivers.get(it);}if (curr == null) {curr = registeredReceivers.get(ir);}if (curr.getPriority() >= curt.priority) {// Insert this broadcast record into the final list.receivers.add(it, curr);ir++;curr = null;it++;NT++;} else {// Skip to the next ResolveInfo in the final list.it++;curt = null;}}}while (ir < NR) {if (receivers == null) {receivers = new ArrayList();}receivers.add(registeredReceivers.get(ir));ir++;}if ((receivers != null && receivers.size() > 0)|| resultTo != null) {BroadcastQueue queue = broadcastQueueForIntent(intent);BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,callerPackage, callingPid, callingUid, resolvedType,requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,resultData, resultExtras, ordered, sticky, false, userId);if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r+ ": prev had " + queue.mOrderedBroadcasts.size());if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,"Enqueueing broadcast " + r.intent.getAction());boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);if (!replaced) {queue.enqueueOrderedBroadcastLocked(r);//处理广播queue.scheduleBroadcastsLocked();}}return ActivityManager.BROADCAST_SUCCESS;}

queue.scheduleBroadcastsLocked(),去处理广播

public void scheduleBroadcastsLocked() {if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["+ mQueueName + "]: current="+ mBroadcastsScheduled);if (mBroadcastsScheduled) {return;}//通过发送mHandler发送消息去处理广播mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));mBroadcastsScheduled = true;}

处理消息

   @Overridepublic void handleMessage(Message msg) {switch (msg.what) {...case BROADCAST_TIMEOUT_MSG: {synchronized (mService) {//处理队列中的广播broadcastTimeoutLocked(true);}} break;}...}

broadcastTimeoutLocked的方法很长,但是重点的函数调用只有两个

final void processNextBroadcast(boolean fromMsg) {...while (mParallelBroadcasts.size() > 0) {//从广播队列中取出广播,准备发出去。r = mParallelBroadcasts.remove(0);//记录广播发送时候的一些时间点r.dispatchTime = SystemClock.uptimeMillis();r.dispatchClockTime = System.currentTimeMillis();mCurrentBroadcast = r;final int N = r.receivers.size();if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["+ mQueueName + "] " + r);for (int i=0; i<N; i++) {Object target = r.receivers.get(i);if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,"Delivering non-ordered on [" + mQueueName + "] to registered "+ target + ": " + r);//发送给已经注册的广播接受者deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);}addBroadcastToHistoryLocked(r);if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["+ mQueueName + "] " + r);mCurrentBroadcast = null;}// 这段代码也是比较重要的,主要是判断当前的进程还不是活着ProcessRecord app = mService.getProcessRecordLocked(targetProcess,info.activityInfo.applicationInfo.uid, false);if (app != null && app.thread != null) {//活着就直接处理广播try {app.addPackage(info.activityInfo.packageName,info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);processCurBroadcastLocked(r, app);return;} catch (RemoteException e) {Slog.w(TAG, "Exception when sending broadcast to "+ r.curComponent, e);} catch (RuntimeException e) {...}// If a dead object exception was thrown -- fall through to// restart the application.}// 走到这里说明接受改广播的app进程没有运行,所以调用startProcessLocked方法去开启一个进程//这里得出一个结论就是,Android的四大组建在启动的时候都会调用App的Application中的oncreate方法,这里就不分析原因里,后面有时间会出一片文章专门分析一下。if ((r.curApp=mService.startProcessLocked(targetProcess,info.activityInfo.applicationInfo, true,r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,"broadcast", r.curComponent,(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))== null) {// Ah, this recipient is unavailable.  Finish it if necessary,// and mark the broadcast record as ready for the next.logBroadcastReceiverDiscardLocked(r);finishReceiverLocked(r, r.resultCode, r.resultData,r.resultExtras, r.resultAbort, false);scheduleBroadcastsLocked();r.state = BroadcastRecord.IDLE;return;}mPendingBroadcast = r;mPendingBroadcastRecvIndex = recIdx;}...
}

BroadcastQueue#deliverToRegisteredReceiverLocked的代码很多,前面主要是一些权限的判断,重要的代码就一行 performReceiveLocked

private void deliverToRegisteredReceiverLocked(BroadcastRecord r,BroadcastFilter filter, boolean ordered) {...performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,new Intent(r.intent), r.resultCode, r.resultData,r.resultExtras, r.ordered, r.initialSticky, r.userId);...}private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,Intent intent, int resultCode, String data, Bundle extras,boolean ordered, boolean sticky, int sendingUser) throws RemoteException {// Send the intent to the receiver asynchronously using one-way binder calls.if (app != null) {if (app.thread != null) {app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,data, extras, ordered, sticky, sendingUser, app.repProcState);} else {// Application has died. Receiver doesn't exist.throw new RemoteException("app.thread must not be null");}} else {receiver.performReceive(intent, resultCode, data, extras, ordered,sticky, sendingUser);}}

app.thread的实现类其实是ApplicationThread里的ActivityThread,所以实际会调用scheduleRegisteredReceiver的

 public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,int resultCode, String dataStr, Bundle extras, boolean ordered,boolean sticky, int sendingUser, int processState) throws RemoteException {RemoteException remoteException = null;...try {receiver.performReceive(intent, resultCode, dataStr, extras, ordered,sticky, sendingUser);return;} catch (RemoteException e) {remoteException = e;}...}

这里我们可以分析一下receiver是在设么地方赋值的?我们可以倒着往上面分析,看一下receiver是从哪里传来的

  1. 首先通过这个变量拿到的filter.receiverList.receiver
  2. 而filter是从r.receivers.get(i)拿到的
  3. 而r是从mParallelBroadcasts.remove(0);里拿出来的。
  4. mParallelBroadcastsi里面其实存放就是我们之前注册的广播,mParallelBroadcasts.add®;
  5. r是我们创建的一个广播记录 BroadcastRecord r = new BroadcastRecord(queue, intent, null,
    null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
    null, 0, null, null, false, true, true, -1);
    6.receivers里面装的是BroadcastFilter,而BroadcastFilter的成员变量ReceiverList也是new出来的,ReceiverList的成员变量receiver是在ContextImpl#registerReceiverInternal方法中new出来的
rd = new LoadedApk.ReceiverDispatcher(receiver, context, scheduler, null, true).getIIntentReceiver();ReceiverDispatcher(BroadcastReceiver receiver, Context context,Handler activityThread, Instrumentation instrumentation,boolean registered) {if (activityThread == null) {throw new NullPointerException("Handler must not be null");}//终于找到赋值的地方了mIIntentReceiver = new InnerReceiver(this, !registered);mReceiver = receiver;mContext = context;mActivityThread = activityThread;mInstrumentation = instrumentation;mRegistered = registered;mLocation = new IntentReceiverLeaked(null);mLocation.fillInStackTrace();}

接着分析广播的发送,从上面的倒推分析我们知道了receiver其实就是LoadedApk.ReceiverDispatcher.InnerReceiver,所以最终调的就是InnerReceiver的performReceive方法

 public void performReceive(Intent intent, int resultCode, String data,Bundle extras, boolean ordered, boolean sticky, int sendingUser) {...rd.performReceive(intent, resultCode, data, extras,ordered, sticky, sendingUser);...}

rd其实是在new InnerReceiver的时候传进来的this,this就是LoadedApk.ReceiverDispatcher,所以最终调到了LoadedApk.ReceiverDispatcher#

public void performReceive(Intent intent, int resultCode, String data,Bundle extras, boolean ordered, boolean sticky, int sendingUser) {...Args args = new Args(intent, resultCode, data, extras, ordered,sticky, sendingUser);if (!mActivityThread.post(args)) {...}}

Args是个runnable,我们来看一下他的run方法,这里我们看到来Broadcast的onReceive方法得到来调用。

 public void run() {...ClassLoader cl =  mReceiver.getClass().getClassLoader();intent.setExtrasClassLoader(cl);setExtrasClassLoader(cl);receiver.setPendingResult(this);receiver.onReceive(mContext, intent);...}

磕磕绊绊,修修补补,广播的注册和发送我们就分析完来,这片文章其实断断续续的写来好几天,这段时间公司的需求比较多,时间也比较紧急,所以没有那么多的闲暇时间来安心的写文章,在工作中很难抽时间是因为老是被各种事情打断,因为是开发Rom,部门之间的沟通相比于开发app关联性很大,经常要和不同的部门打交道,也就没有那么多的时间了,本来想着这篇文章就不打算画流程图来,因为流程图意味着要把所有的逻辑再梳理一下,这时候大脑给我的信号是决绝去画流程图的,今天周末,正好又闲暇的时间来再把流程图补充上。

  • 流程图地址

Android源码分析之广播的发送和接收流程相关推荐

  1. tcp/ip 协议栈Linux内核源码分析15 udp套接字接收流程二

    内核版本:3.4.39 上篇我们分析了UDP套接字如何接收数据的流程,最终它是在内核套接字的接收队列里取出报文,剩下的问题就是谁会去写入这个队列,当然,这部分工作由内核来完成,本篇剩下的文章主要分析内 ...

  2. bluedroid源码分析之ACL包发送和接收(二)

    更多内容请参照我的个人站点: http://stackvoid.com/ 上一节讲了数据流入口,本文分析L2CAP的处理函数. L2CAP层的处理 我们的音乐数据,通过 L2CAP 入口函数 l2c_ ...

  3. Android源码分析--MediaServer源码分析(二)

    在上一篇博客中Android源码分析–MediaServer源码分析(一),我们知道了ProcessState和defaultServiceManager,在分析源码的过程中,我们被Android的B ...

  4. 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析

    [Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...

  5. Android 源码分析

    查看源码版本号: build\core\version_defaults.mk //搜索该文件中的 PLATFORM_VERSION值 frameworks 目录 (核心框架--java及C++语言) ...

  6. Android源码分析 - Framework层的Binder(客户端篇)

    开篇 本篇以aosp分支android-11.0.0_r25作为基础解析 我们在之前的文章中,从驱动层面分析了Binder是怎样工作的,但Binder驱动只涉及传输部分,待传输对象是怎么产生的呢,这就 ...

  7. Android源码分析-全面理解Context

    前言 Context在android中的作用不言而喻,当我们访问当前应用的资源,启动一个新的activity的时候都需要提供Context,而这个Context到底是什么呢,这个问题好像很好回答又好像 ...

  8. Android源码分析(十一)-----Android源码中如何引用aar文件

    一:aar文件如何引用 系统Settings中引用bidehelper-1.1.12.aar 文件为例 源码地址:packages/apps/Settings/Android.mk LOCAL_PAT ...

  9. Android源码分析(三)-----系统框架设计思想

    一 : 术在内而道在外 Android系统的精髓在源码之外,而不在源码之内,代码只是一种实现人类思想的工具,仅此而已...... 近来发现很多关于Android文章都是以源码的方向入手分析Androi ...

最新文章

  1. C标准库 limits.h
  2. 直播内容不合规怎么办?智能AI为您解决审核难题
  3. ubuntu9.04换源
  4. 新事务不能登记到指定的事务处理器中异常的处理----MSDTC的正确配置
  5. 几个常用的JS代码.
  6. oracle 查看函数被哪些触发器引用_oracle如何查看存储过程,存储函数,触发器的具体内容...
  7. rpc结构错误_结构性错误
  8. (转)使用tar和split打包分割文件
  9. java中加载窗口的函数_Java函数调用 - playgame的个人页面 - OSCHINA - 中文开源技术交流社区...
  10. Linux添加1G虚拟内存,Linux下怎样增加虚拟内存
  11. Docker使用小结(四)发布镜像
  12. 在php里怎么安装composer,怎么安装composer
  13. 重庆钢铁泛微oa系统服务器更新时间,泛微全新OA系统-协同办公系统
  14. Latex各种箭头符号总结
  15. mysql实验报告4_实验四∶数据库安全性实验报告.doc
  16. 甲骨文公司总裁Larry Ellison在耶鲁大学的演讲
  17. Swift 类和结构体总结
  18. 报表平台——体系架构
  19. TOMVIP邮箱贴心服务,祝您做好邮件营销
  20. 通过两个小例子,更快了解-Xms -Xmx

热门文章

  1. Lesson01: 网页脚本语言
  2. SOHO中国董事长潘石屹的昨天今天和明天
  3. 云课堂智慧职教计算机基础答案,2020智慧职教云课堂计算机文化基础答案最新最全课后作业答案...
  4. B2B与B2C区别有哪些?
  5. STDIN_FILENO 与stdin的区别
  6. 多特征融合的高分辨率遥感图像海陆分离——刘思彤
  7. html扣图标,怎么用PS抠图标?
  8. 2005年,鼹鼠来了!!!
  9. hdu 5655(拼四边形)
  10. 软件设计经典书籍推荐