相信做过Android开发的人一定接触过广播相关的操作,从注册广播接收者到发送广播等一系列过程,本系列文章主要是向大家介绍广播背后的源码分析过程,广播相关的注意事项以及如何去解决广播相关的问题,本系列总共分为三个部分:

  • Android—广播(Broadcast)—广播接收者的注册过程分析
  • Android—广播(Broadcast)—广播的发送过程分析
  • Android—广播(Broadcast)—广播的注意事项及相关问题分析

  Android 广播机制的实现采用了观察者模式,基于消息的发布和订阅的模型,整套流程的核心操作都在ActivityManagerService(以下简称AMS),大致的流程如下:

  1.客户端将BroadcastReceiver通过Binder机制向AMS注册。

  2.广播的发送者通过Binder机制向AMS发送广播。

  3.AMS根据广播查找到相关的BroadcastReceiver,并通过发送消息将其放入消息循环队列中,然后通过异步方式来把广播发送给相应的接收者。

  4.消息被执行时,最终会回调BroadcastReceiver中的onReceive()方法。

  本系列是基于Android 6.0的代码进行分析,下面就先分析广播接收者的注册过程。

  通常注册广播接收者只需要在Activity或者Service中调用registerReceiver()来实现,其实不管是从哪里调用,两者最终都是会调用到ContextImpl.java中的registerReceiver(),然后内部通过Binder机制调用到AMS中的registerReceiver()方法,其实从下面的流程图看出,整个注册过程还是比较简单的。

Android 判断广播未注册_java

  ContextImpl.java中的registerReceiver()最终调用到了registerReceiverInternal()方法:
ContextImpl.java

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
        IntentFilter filter, String broadcastPermission,
        Handler scheduler, Context context) {
    //IIntentReceiver 接口对象rd是一个binder对象,会被传入到AMS中,AMS在收到相应广播后,就是通过
    //这个rd再通知到客户端这边去接收广播
    IIntentReceiver rd = null;
    if (receiver != null) {
        //mPackageInfo是一个LoadedApk对象, 其在ContextImpl初始化的时候就已经赋值了,context对象
        //也即最开始调用registerReceiver()的activity或者service对象
        if (mPackageInfo != null && context != null) {
            if (scheduler == null) {
                //也就是ActivityThread.java中的mH对象
                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;
    }
}

  通过mPackageInfo.getReceiverDispatcher()获取到IIntentReceiver接口对象rd,从下面实现可以看出,rd就是InnerReceiver类的对象。
LoadedApk.java

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;
        if (registered) {
            map = mReceivers.get(context);
            if (map != null) {
                rd = map.get(r);
            }
        }
        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 {
            rd.validate(context, handler);
        }
        rd.mForgotten = false;
        return rd.getIIntentReceiver();
    }
}

ReceiverDispatcher(BroadcastReceiver receiver, Context context,
        Handler activityThread, Instrumentation instrumentation,
        boolean registered) {
    ...
    //这个就是最终传给AMS端的binder代理对象
    mIIntentReceiver = new InnerReceiver(this, !registered);
    ...
}

IIntentReceiver getIIntentReceiver() {
    return mIIntentReceiver;
}

final static class InnerReceiver extends IIntentReceiver.Stub {
    ...
}

  等到rd准备好后,在ContextImpl.registerReceiverInternal()最后调用了ActivityManagerNative.getDefault().registerReceiver()方法,并把相关对象传入到函数参数中,最终通过Binder调用到了AMS中的registerReceiver()方法里。
  粗看registerReceiver()方法,发现函数体的实现代码比较偏多,经常分析framework代码就知道,framework中很多函数的实现代码都非常多,看上去确实挺让人崩溃的,对于实现代码比较偏多的函数,我个人认为比较好的分析方式是对函数进行分段来分析,先大致浏览函数的所有代码,对函数有一个大致的了解,然后按照执行顺序分出对应的几个大的段,针对每段进行具体的分析。
  这里我将registerReceiver()分成四个部分来分析,分别是:检查调用者的合法性和获取对应的Uid及Pid,搜集sticky广播对应的Intent,给注册进来的receiver创建对应的对象并保存,发送sticky广播给receiver,下面就基于这四个部分来分析registerReceiver()的实现。

  • 检查调用者的合法性和获取对应的Uid及Pid
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();
        }

   这部分其实就是检查调用者所在的进程是否存在,如果callerApp不存在,也就是调用者所在的进程都没有,那肯定是不合法的,或者进程块callerApp存在,但是callerApp里面没有callerPackage,也即调用者的包名都不在进程里面,肯定也是不合法的。检查完后再获取对应的callingUid和callingPid,如果caller直接为null,那么此时的uid和pid直接取binder调用者的uid和pid即可。

  • 搜集sticky广播对应的Intent

ActivityManagerService.java

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 users
    int[] 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...
    //尽管上面已经根据action查找到了相应的intent,但是此处还是要继续检查intent是否跟filter相匹配
    for (int i = 0, N = stickyIntents.size(); i < N; i++) {
        Intent intent = stickyIntents.get(i);
        // If intent has scheme "content", it will need to acccess
        // provider that needs to lock mProviderMap in ActivityThread
        // and also it may need to wait application response, so we
        // cannot lock ActivityManagerService here.
        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;
}

   这里涉及到sticky这种类型的广播,大致意思就是这种类型的广播发出后会留在AMS中,等待后续如果有注册者的话就会继续把广播发送给它,更加详细的介绍可以参考 ,里面的mStickyBroadcasts是一个SparseArray类型的对象,其key即当前的User id,在后面介绍广播的发送时会知道,在发送sticky类型的广播时会记录到此变量中,不过这类广播从Android 5.0开始,相关的发送sticky广播函数都已经废弃了,现在用的比较多的主要都是一些框架内部的广播。

  • 给注册进来的receiver创建对应的对象并保存

ActivityManagerService.java

synchronized (this) {
    if (callerApp != null && (callerApp.thread == null
            || callerApp.thread.asBinder() != caller.asBinder())) {
        // Original caller already died
        return 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.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");
    }
    mReceiverResolver.addFilter(bf);

   这部分算是registerReceiver()方法的核心部分了,这里的很多实现都是为了后续发送广播服务的。
   在注册广播接收者时,除了要传入一个receiver外,还需要一个IntentFilter,用来告知要监听哪些广播,另外有可能同一个receiver,跟多个不同的IntentFilter一同注册进来,所以ReceiverList 是一个ArrayList,而BroadcastFilter是继承自IntentFilter,BroadcastFilter起到了把receiver跟IntentFilter关联起来的作用,mRegisteredReceivers是以receiver为key,ReceiverList为value的HashMap对象,用来存储一个receiver及其对应的所有IntentFilter,最后把bf加入到mReceiverResolver中,这几个变量在后续发送广播时查找广播的接收者会经常用到,

  • 发送sticky广播给receiver

ActivityManagerService.java

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

   最后一部分是发送sticky类型的广播,根据intent得到对应的BroadcastQueue,然后调用其scheduleBroadcastsLocked()方法将广播发送出去,这部分也是下一篇需要重点讲解的内容,所以这里就先不详细展开了,请大家直接移步下一篇文档。
   最后,整个广播接收者的注册过程就结束了,回顾一下,总的来说还是比较简单,客户端传入一个Binder对象给AMS服务端作为标识,然后服务端内部会根据传入的receiver以及filter新建或者创健相关对象并放入到指定的AMS内部成员中,这些都是为了后续发送广播服务的。下一节我们就开始详细介绍广播的发送流程。

参考资料
Android总结篇系列:Android广播机制
Android系统中的广播(Broadcast)机制简要介绍和学习计划
Android应用程序注册广播接收器(registerReceiver)的过程分析
sendStickyBroadcast 的理解和使用