Android 7.0 ActivityManagerService 广播(Broadcast)相关流程分析

来源:互联网 发布:linux lvm扩容 编辑:程序博客网 时间:2024/06/05 19:38

参考:(Android 7.0 ActivityManagerService(5) 广播(Broadcast)相关流程分析)

总结:

1. BroadcastReceiver的注册类型静态注册动态注册

2. 广播的种类

普通广播 
普通广播由发送方调用sendBroadcast及相关重载函数发送。

AMS转发这种类型的广播时,根据BroadcastReceiver的注册方式,进行不同的处理流程。 

  • 对于动态注册的广播,理论上可以认为,AMS将在同一时刻,向所有监听此广播的BroadcastReceiver发送消息,因此整体的消息传递的效率比较高。 
  • 对于静态注册的广播,AMS将按照有序广播的方式,向BroadcastReceiver发送消息

有序广播 
有序广播由发送方调用sendOrderedBroadcast及相关重载函数发送,是一种串行的广播发送方式。

处理这种类型的广播时,AMS会按照优先级,将广播依次分发给BroadcastReceiver。 
AMS收到上一个BroadcastReceiver处理完毕的消息后,才会将广播发送给下一个BroadcastReceiver。 
其中,任意一个BroadcastReceiver,都可以中止后续的广播分发流程。 
同时,上一个BroadcastReceiver可以将额外的信息添加到广播中。

前文已经指出,当普通广播发送给静态注册的BroadcastReceiver时,AMS实际上是按照有序广播的方式来进行发送

粘性广播 
粘性广播由发送方调用sendStickyBroadcast及相关重载函数发送。 
需要注意的是,目前这种广播已经被附上Deprecated标签了,不再建议使用。

粘性广播和有序广播等概念实际上不是冲突的。 
粘性仅仅强调系统将会保存这个广播,它的其它处理过程与上文一致。

3. 适时地限制广播发送范围

在某些场景下,这种设计也可能带来一些安全隐患: 
1.其它App可能会针对性的发出与当前App intent-filter相匹配的广播,导致当前App不断接收到广播并处理; 
2.其它App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。

因此,在必要的时候,需要适时地限制广播发送范围,例如: 

  1. 对于同一App内部发送和接收广播,将exported属性设置成false; 
  2. 在广播发送和接收时,都增加上相应的permission; 
  3. 发送广播时,指定BroadcastReceiver对应的包名。

实际上,在framework/support/v4/java/android/support/v4/content目录下,Android定义了LocalBroadcastManager类,用于发送和接收仅在同一个App应用内传递的广播


4. BroadcastQueue是负责添加broadcast,并处理broadcast的主要类。

其中包含两个队列,分别用于处理串行广播和并行广播。

   final ArrayList<BroadcastRecord>mParallelBroadcasts = new ArrayList<BroadcastRecord>();
    final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<BroadcastRecord>();


队列里存放的是BroadcastRecord,该record中有两个重要的变量,

    final Intent intent;   广播对应的intent

    final List receivers;    该广播所有的接收者,接收者有两种类型,即静态receiver和动态receiver

动态receiver使用BroadcastFilter结构,静态receiver使用ResolveInfo。


动态receiver的处理流程

InBroadcastQueue.java

    private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser)
        if (app != null) {
            if (app.thread != null) {
                app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                        data, extras, ordered, sticky, sendingUser, app.repProcState);
    }

In ActivityThread.java

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

In LoadedApk.java

        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)) {
            }
        }

        final class Args extends BroadcastReceiver.PendingResult implements Runnable {
            
            public void run() {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveReg");
                try {
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    receiver.onReceive(mContext, intent);  在ActivityThread类的UI thread中执行 BroadcastReceiver.onReceive 回调函数
                } catch (Exception e) {
                }
                
                if (receiver.getPendingResult() != null) {     一般情况下,这个判断都返回true,但是就先不通知AMS,由用户自己的代码来通知AMS(调用PendingResult.finish函数通知AMS)。
                    finish();         通知AMS,当前广播已经处理完毕,可以处理下一个receiver了
                }
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
        }


静态receiver的处理流程

InBroadcastQueue.java

    private final void processCurBroadcastLocked(BroadcastRecord r,
            ProcessRecord app) throws RemoteException
        boolean started = false;
        try {
            mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
            app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                    mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
                    r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
                    app.repProcState);
            started = true;
        } finally {
            if (!started) {
                r.receiver = null;
                r.curApp = null;
                app.curReceiver = null;
            }
        }
    }

In ActivityThread.java

        public final void scheduleReceiver(Intent intent, ActivityInfo info,
                CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                boolean sync, int sendingUser, int processState) {
            updateProcessState(processState, false);
            ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                    sync, false, mAppThread.asBinder(), sendingUser);
            r.info = info;
            r.compatInfo = compatInfo;
            sendMessage(H.RECEIVER, r);
        }

                case RECEIVER:
                    handleReceiver((ReceiverData)msg.obj);
                    break;

    private void handleReceiver(ReceiverData data) {

        String component = data.intent.getComponent().getClassName();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);

        IActivityManager mgr = ActivityManagerNative.getDefault();

        BroadcastReceiver receiver;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.setExtrasClassLoader(cl);
            receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
        }

        try {
            Application app = packageInfo.makeApplication(false, mInstrumentation);

            ContextImpl context = (ContextImpl)app.getBaseContext();
            sCurrentBroadcastIntent.set(data.intent);
            receiver.setPendingResult(data);
            receiver.onReceive(context.getReceiverRestrictedContext(), data.intent);
        }

        if (receiver.getPendingResult() != null) {一般情况下,这个判断都返回true,但是就先不通知AMS,由用户自己的代码来通知AMS(调用PendingResult.finish函数通知AMS)。
            data.finish();    通知AMS,当前广播已经处理完毕,可以处理下一个receiver了
        }
    }

onReceive函数超时的处理

对于一个静态注册的BroadcastReceiver 对象,对应的进程初始时可能并没有启动。 
当AMS向这个BroadcastReceiver对象发送广播时,才会先启动对应的进程。 
一旦BroadcastReceiver对象处理完广播,i并将返回结果通知给AMS后,AMS就有可能清理掉对应进程。 
因此,若在onReceive中创建线程处理问题,那么onReceive函数就可能在线程完成工作前返回,导致AMS提前销毁进程。 
此时,线程也会消亡,使得工作并没有有效完成。


Ps: why AMS kill the process

Because AMS will set the state for process according to some criteria. One of the criteria is whether the process is processing a broadcast. After onReceive method return, AMS know that the process has finished processing broadcast. So AMS will treat the process as an empty process.

AMS will kill empty process is the number of empty process exceed a limit.

Refer to "ProcessRecord state"


为了解决这个问题,就可以用到BroadcastReceiver提供的PendingResult。 
具体的做法是: 
先调用BroadcastReceiver的goAsync函数得到一个PendingResult对象 
然后将该对象放到工作线程中去释放。 
这样onReceive函数就可以立即返回而不至于阻塞主线程。 
同时,Android系统将保证BroadcastReceiver对应进程的生命周期, 
直到工作线程处理完广播消息后,调用PendingResult的finish函数为止。

其中原理是: 
正常情况下,BroadcastReceiver在onReceive函数结束后, 
判断PendingResult不为null,才会将处理完毕的信息通知给AMS。

一旦调用BroadcastReceiver的goAsync函数,就会将BroadcastReceiver中的PendingResult置为null, 
因此即使onReceive函数返回,也不会将信息通知给AMS。 
AMS也就不会处理BroadcastReceiver对应的进程。

待工作线程调用PendingResult的finish函数时,才会将处理完毕的信息通知给AMS。

orderedBroadcast的超时处理

前台广播和后台广播的超时时间是不同的

        mFgBroadcastQueue = newBroadcastQueue(this, mHandler, 前台广播队列,超时时间为10s
                "foreground", BROADCAST_FG_TIMEOUT, false);
        mBgBroadcastQueue = new BroadcastQueue(this, mHandler,  后台广播队列,超时时间为60s

                "background", BROADCAST_BG_TIMEOUT, true);

每个BroadcastRecord中有几个变量用于超时处理。

    long dispatchTime;      // when dispatch started on this set of receivers,当前intent对应的receivers中,处理第一个receiver的时间点
    long receiverTime;      // when current receiver started for timeouts.处理每个receiver的开始时间点,每个receiver处理前都会更新这个值

处理每个receiver之前,都会向自己的message queue中发送一个延时message。

    final void setBroadcastTimeoutLocked(long timeoutTime) {
        if (! mPendingBroadcastTimeoutMessage) {
            Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
            mHandler.sendMessageAtTime(msg, timeoutTime);
            mPendingBroadcastTimeoutMessage = true;
        }
    }

当message到时间后,就会被处理,在处理中,会判断当前时间是否超过了当前receiver的超时时间当前receiver的超时时间 =BroadcastRecord.

receiverTime +超时间隔,10s 或者 60s)。如果确定超时,则调用 broadcastTimeoutLocked 函数,通知AMS,broadcast ANR。

0 0
原创粉丝点击