Android四大组件——BroadCastReceiver的工作过程

BroadCastReceiver的工作过程分为包含两方面的内容:

  1. 注册
  2. 发送和接收

使用BroadcastReceiver很简单,只需要继承BroadcastReceiver并重写它的onReceive方法即可。

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        //TODO something
    }
}

为了使BroadcastReceiver工作,就需要对它进程注册。注册BroadCastReceiver分为在AndroidManifest.xml静态注册和代码中动态注册。

静态注册示例:

<receiver android:name=".MyReceiver">
    <intent-filter>
        <action android:name="com.sososeen.09.demo.my.receiver"/>
    </intent-filter>
</receiver>

动态注册也很简单:

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.sososeen.09.demo.my.receiver");
registerReceiver(new MyReceiver(), intentFilter);

不过动态注册别忘了反注册,否则会造成内存泄漏。

发送广播示例如下,通过给Intent设置action,对应的广播接收者的onReveive就会被回调了。

Intent intent = new Intent();
intent.setAction("com.sososeen.09.demo.my.receiver");
sendBroadcast(intent);

注册广播和发送广播中间过程是怎么进行的,我们今天就来分析一下。由于静态注册牵涉到在应用安装时有系统自动完成注册,确切的说是通过PackageManagerService完成注册,相对比较复杂。今天先来分析一下动态注册的BroadCastReceiver的工作过程。

在本文的分析中,ActivityManagerService简称AMS。

动态注册BroadCastReceiver

注册广播接收者是通过ContextWrapper来进行的,但是类似启动Service,真正干事的还是ContextImpl,我们直接来看它的方法。2个参数的registerReceiver方法,会调用4个参数的registerReceiver方法,然后会调用registerReceiverInternal方法。

# android.app.ContextImpl
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    return registerReceiver(receiver, filter, null, null);
}

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
                               String broadcastPermission, Handler scheduler) {
    return registerReceiverInternal(receiver, getUserId(),
            filter, broadcastPermission, scheduler, getOuterContext());
}

在registerReceiverInternal方法中主要干了两件事:

  1. mPackageInfo指的是LoadedApk对象,通过调用LoadedApk的getReceiverDispatcher方法把BroadcastReceiver包装为ReceiverDispatcher.InnerReceiver对象,这是因为BroadcastReceiver是不具有跨进程通信能力的,想要最终调用到BroadcastReceiver的onReceive方法,必须有一个Binder对象用来进行IPC,在这里就是ReceiverDispatcher.InnerReceiver对象。InnerReceiver对象持有ReceiverDispatcher对象的引用。当AMS端调用InnerReceiver的接收广播的方法,InnerReceiver对象会找到对应的ReceiverDispatcher进而找到BroadcastReceiver对象并调用其onReceive方法。这个过程我们后面再分析。这种方式与bind方式启动Service的中包装ServiceConnection是类似的,有兴趣的可以看一下Android四大组件——Service的工作过程分析

  2. ActivityManagerNative.getDefault()获取的是AMS在本地的代理,该代理通过IPC过程调用AMS的registerReceiver方法。

# android.app.ContextImpl
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) {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            // 1 创建ReceiverDispatcher.InnerReceiver对象
            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 {
        // 2 IPC过程调用AMS的registerReceiver方法
        final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
                mMainThread.getApplicationThread(), mBasePackageName,
                rd, filter, broadcastPermission, userId);
        if (intent != null) {
            intent.setExtrasClassLoader(getClassLoader());
            intent.prepareToEnterProcess();
        }
        return intent;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

我们先来看一下LoadedApk的getReceiverDispatcher方法,就是创建一个ReceiverDispatcher对象并将其保存起来,在ReceiverDispatcher的构造方法中创建InnerReceiver对象,ReceiverDispatcher对象中保存了BroadcastReceiver和InnerReceiver对象。


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();
    }
}

我们来看一下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;
      ...
        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);
        } 
        ...
        BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                permission, callingUid, userId);
        rl.add(bf);
       ...
        mReceiverResolver.addFilter(bf);
 ...
    }
}

在上面的方法中
mRegisteredReceivers 是一个HashMap对象,用于追踪客户端BroadcastReceiver对应的ReceiverDispatcher.InnerReceiver对象。ReceiverList是一个ArrayList集合,用于存放BroadcastFilter,BroadcastFilter用于包装IntentFilter。我们需要知道一点,同一个BroadcastReceiver是可以对应多个IntentFilter的,只要匹配一个就能够接收消息。

final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();

说明: BroadcastFilter中封装了ReceiverList对象,而ReceiverList对象中包含了客户端BroadcastReceiver对应的IIntentReceiver对象(实际上就是ReceiverDispatcher.InnerReceiver对象),将来在发送广播的阶段,通过Intent找到所有匹配到的动态注册的BroadcastFilter集合。

到此,BroadcastReceiver的动态注册过程就完毕了。

广播的发送和接收过程

广播的发送分为普通广播、有序广播和粘性广播。有序广播是按照广播接收者的优先级,从高优先级到低优先级依次接收,而且在高优先级的广播接收者中是可以打断广播,造成低优先级的广播接收不到。而粘性广播表示想要当发送广播的时候,BroadcastReceiver还没有创建,当BroadcastReceiver创建后,它的onReveive方法会被立即调用。

在这里我们分析一下普通广播的发送和接收流程。

调用Context的方法发送广播,实际上还是会调用ContextImpl的方法,来一下ContextImpl的sendBroadcast方法

@Override
public void sendBroadcast(Intent intent) {
    warnIfCallingFromSystemProcess();
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        intent.prepareToLeaveProcess(this);
        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 e.rethrowFromSystemServer();
    }
}

从这个过程中看,通过一个IPC过程调用到AMS的broadcastIntent方法,在该方法又调用了AMS的broadcastIntentLocked方法。

public final int broadcastIntent(IApplicationThread caller,
                                 Intent intent, String resolvedType, IIntentReceiver resultTo,
                                 int resultCode, String resultData, Bundle resultExtras,
                                 String[] requiredPermissions, int appOp, Bundle bOptions,
                                 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, bOptions, serialized, sticky,
                callingPid, callingUid, userId);
        Binder.restoreCallingIdentity(origId);
        return res;
    }
}

broadcastIntentLocked方法非常长,我们尽量选取一些关键的地方来分析,在方法的开始会有如下代码:

intent = new Intent(intent);

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

表示会重新创建一个Intent对象用于包裹传递过来的的Intent对象,并且为Intent添加了一个flag,Intent.FLAG_EXCLUDE_STOPPED_PACKAGES,表示默认情况下广播不会发送给已经停止运行的App,这也是为了方式一些App想要利用广播来启动进程。

从Android3.1开始,添加了两个标记

// 表示不包含已经停止的应用,如果广播设置这个标记,就不会发送给已经停止的应用
public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;
 
// 表示包含已经停止的应用,如果广播设置这个标记,就会发送给已经停止的应用
public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;

从Android 3.1开始,默认情况下都是添加一个FLAG_EXCLUDE_STOPPED_PACKAGES标记,表示不会发送给停止的应用,如果确实需要的话,需要给广播显式的设置FLAG_INCLUDE_STOPPED_PACKAGES标记。

在broadcastIntentLocked内部,会根据IntentFilter查找出匹配的BroadcastReceiver,经过一系列条件的筛选和过滤,将满足条件的接收者放在BroadcastQueue中,接下来BroadcastQueue就会将广播发送出去。注意在这个过程中会先把找出来的动态注册的广播发出去,然后再把静态注册的广播发出去。因此,动态注册的广播优先级是高于静态注册的广播的。而且,如果BroadcastReceiver同时注册了静态广播和动态广播,其onReceive方法会被调用两次。

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();
    }

BroadcastQueue发送广播的实现如下,通过Handler发送一个BROADCAST_INTENT_MSG类型的消息,Handler收到消息之后会调用BroadcastQueue的processNextBroadcast方法。

public void scheduleBroadcastsLocked() {
    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
            + mQueueName + "]: current="
            + mBroadcastsScheduled);

    if (mBroadcastsScheduled) {
        return;
    }
    mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
    mBroadcastsScheduled = true;
}

BroadcastQueue的processNextBroadcast方法很长,我们选取发送普通广播的部分来看,可以看到无序广播存放在mParallelBroadcasts集合中,通过遍历这个集合来发送广播给BroadcastReceiver。具体的发送过程是deliverToRegisteredReceiverLocked方法。

final void processNextBroadcast(boolean fromMsg) {
       ...
        // First, deliver any non-serialized broadcasts right away.
        while (mParallelBroadcasts.size() > 0) {
            r = mParallelBroadcasts.remove(0);
            r.dispatchTime = SystemClock.uptimeMillis();
            r.dispatchClockTime = System.currentTimeMillis();
            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, i);
            }
            addBroadcastToHistoryLocked(r);
            if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
                    + mQueueName + "] " + r);
        }

再来看一下BroadcastQueue的deliverToRegisteredReceiverLocked方法,该方法负责将一个特定的广播发送给特定的接收者,具体的发送过程是调用了performReceiveLocked方法。

performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);

来看一下performReceiveLocked方法,app.thread指的就是ApplicationThread在本地的代理对象,app.thread != null 条件满足,会通过IPC过程调用ApplicationThread的scheduleRegisteredReceiver方法。

# com.android.server.am.BroadcastQueue

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) {
            // If we have an app thread, do the call through that so it is
            // correctly ordered with other one-way calls.
            try {
                app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                        data, extras, ordered, sticky, sendingUser, app.repProcState);
              ...
            } catch (RemoteException ex) {
              ...
            }
        } 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);
    }
}

在ApplicationThread的scheduleRegisteredReceiver方法中调用了receiver的performReceive方法,而这个receiver我们知道,实际上就是ReceiverDispatcher.InnerReceiver对象。

public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
                                       int resultCode, String dataStr, Bundle extras, boolean ordered,
                                       boolean sticky, int sendingUser, int processState) throws RemoteException {
    updateProcessState(processState, false);
    receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
            sticky, sendingUser);
}

来看一下ReceiverDispatcher.InnerReceiver对象的performReceive方法,在该方法中首先获取InnerReceiver持有的ReceiverDispatcher对象,如果ReceiverDispatcher对象不为空的话就会调用它的performReceive方法。

# android.app.LoadedApk.ReceiverDispatcher.InnerReceiver
@Override
public void performReceive(Intent intent, int resultCode, String data,
                           Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
    final LoadedApk.ReceiverDispatcher rd;
    if (intent == null) {
        Log.wtf(TAG, "Null intent received");
        rd = null;
    } else {
        rd = mDispatcher.get();
    }
...
    if (rd != null) {
        rd.performReceive(intent, resultCode, data, extras,
                ordered, sticky, sendingUser);
    } else {
  ...
    }
}

在ReceiverDispatcher的performReceive方法中,会封装一个Args对象,Args是ReceiverDispatcher的非静态内部类,因此持有ReceiverDispatcher的引用,可以访问ReceiverDispatcher的参数和方法,并且它实现了Runnable接口。mActivityThread是一个Handler,通过post方法,把调用切换到主线程中来,Args的run方法会被执行。

# android.app.LoadedApk.ReceiverDispatcher
public void performReceive(Intent intent, int resultCode, String data,
                           Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
    final Args args = new Args(intent, resultCode, data, extras, ordered,
            sticky, sendingUser);
    if (intent == null) {
        Log.wtf(TAG, "Null intent received");
    } else {
      ...
    }
    if (intent == null || !mActivityThread.post(args)) {
        if (mRegistered && ordered) {
            IActivityManager mgr = ActivityManagerNative.getDefault();
            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                    "Finishing sync broadcast to " + mReceiver);
            args.sendFinished(mgr);
        }
    }
}

Args的run方法如下,最后会调用BroadcastReceiver的onReceive方法。

# android.app.LoadedApk.ReceiverDispatcher.Args
public void run() {
    final BroadcastReceiver receiver = mReceiver;
...
    ClassLoader cl =  mReceiver.getClass().getClassLoader();
    intent.setExtrasClassLoader(cl);
    intent.prepareToEnterProcess();
    setExtrasClassLoader(cl);
    receiver.setPendingResult(this);
    receiver.onReceive(mContext, intent);
...
}

到此,广播的发送和接收过程也分析完毕了。

总结

  1. 注册广播的时候是把BroadcastReceiver封装到ReceiverDispatcher中,并且把与此对应的ReceiverDispatcher.InnerReceiver作为一个Binder对象传递到AMS端,AMS端会通过一个Map集合来存放ReceiverDispatcher.InnerReceiver对象和对应的IntentFilter包装类。

  2. 发送广播的时候,通过一个IPC过程调用到AMS端,AMS端会找到匹配的广播接收者并添加到BroadcastQueue中,在BroadcastQueue中进程处理之后通过一个IPC过程调用到ApplicationThread的scheduleRegisteredReceiver方法,然后会调用ReceiverDispatcher.InnerReceiver的方法,再经过ReceiverDispatcher、以及它的内部类Args,最终调用到BroadcastReceiver的onReveive方法。

  3. 动态注册的广播优先级是高于静态注册的广播的。而且,如果BroadcastReceiver同时注册了静态广播和动态广播,其onReceive方法会被调用两次。

  4. 从过程中也可以看到,AMS端如果想要与客户端打交道的话都是通过ApplicationThread。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容