Android 廣播內部機制詳解(三)

4 廣播的處理

廣播的處理都會調用BroadcastQueue下的scheduleBroadcastsLocked(),接下我們來看看這個函數

4.1 scheduleBroadcastsLocked

文件:BroadcastQueue.java

    public void scheduleBroadcastsLocked() {
        if (mBroadcastsScheduled) {
            return;
        }
        // 見4.1.1
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }

mBroadcastsScheduled這個boolean的意思是如果當前真好有一個BROADCAST_INTENT_MSG在執行,那麼mBroadcastsScheduled爲true,否則爲false

4.1.1 BroadcastHandler

文件:BroadcastQueue.java

private final class BroadcastHandler extends Handler {
        ......

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BROADCAST_INTENT_MSG: {
                    // 見4.2
                    processNextBroadcast(true);
                } break;

                ......

            }
        }
    }

最重要的就是processNextBroadcast,廣播處理會多次調用它

4.2 processNextBroadcast

文件:BroadcastQueue.java
這個函數有500多行,我們分開來講

4.2.1 非串行廣播的處理

            // 如果是從BroadcastHandler中調用來的,則fromMsg爲true,mBroadcastsScheduled爲false
            if (fromMsg) {
                mBroadcastsScheduled = false;
            }

            // 首先,立即發送所有的非串行廣播
            while (mParallelBroadcasts.size() > 0) {
                r = mParallelBroadcasts.remove(0); // 每次都從一個開始
                r.dispatchTime = SystemClock.uptimeMillis();  // 分發時間
                r.dispatchClockTime = System.currentTimeMillis();  // 分發時間
                final int N = r.receivers.size();

                for (int i=0; i<N; i++) {
                    Object target = r.receivers.get(i);
                    // 將廣播發給BroadcastRecord所有的Receiver,見4.3
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
                }
                addBroadcastToHistoryLocked(r); //將廣播添加歷史統計
            }

這部分主要是對非串行廣播進行發送,通過兩層循環來發送所有的Receiver,接下來看看串行發送的廣播

4.2.2 串行廣播的處理

            if (mPendingBroadcast != null) {

                boolean isDead;
                synchronized (mService.mPidsSelfLocked) {
                    // 拿到正在處理廣播的進程,並判斷它是否死亡
                    ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
                    isDead = proc == null || proc.crashing;
                }
                if (!isDead) {
                    // 如果處理廣播的進程保持活躍狀態,則繼續等待
                    return;
                } else {
                    mPendingBroadcast.state = BroadcastRecord.IDLE;
                    mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
                    mPendingBroadcast = null;
                }
            }

判斷處理廣播的進程是否保持活躍狀態,如果是,則繼續等待其完成

            boolean looped = false;

            do {
                if (mOrderedBroadcasts.size() == 0) {
                    // 所有串行廣播執行完畢,則執行GC操作
                    mService.scheduleAppGcsLocked();
                    if (looped) {
                        mService.updateOomAdjLocked();
                    }
                    return;
                }
                // 拿串行廣播中的第一個
                r = mOrderedBroadcasts.get(0);
                boolean forceReceive = false;

                // 該ReceiverList中所有接收者的數量
                int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
                // AMS已經註冊啓動完成,並且不是該r第一次進這個循環了
                if (mService.mProcessesReady && r.dispatchTime > 0) {
                    long now = SystemClock.uptimeMillis()
                    // 出現超時
                    if ((numReceivers > 0) &&
                            (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                        broadcastTimeoutLocked(false); // 強制結束這個廣播
                        forceReceive = true;
                        r.state = BroadcastRecord.IDLE;
                    }
                }
                if (r.state != BroadcastRecord.IDLE) {
                    return;
                }

每次都會取mOrderedBroadcasts中的第一個元素,然後判斷是否超時,如果超時,則強制結束這個廣播,並且這個BroadcastRecord剩下的廣播接收器也將收不到廣播。超時的依據是:2*mTimeoutPeriod*numReceivers,其中mTimeoutPeriod爲(前臺廣播爲10s,後臺廣播爲60s),numReceivers爲接收器的數量;接着如果BroadcastRecord的state不等於BroadcastRecord.IDLE,就返回

                // 要麼是廣播已經向所有的receiver發送完畢,要麼是被強制停止
                if (r.receivers == null || r.nextReceiver >= numReceivers
                        || r.resultAbort || forceReceive) {
                    if (r.resultTo != null) {
                        try {
                            // 處理廣播消息,調用onReceive(),見4.3.1
                            performReceiveLocked(r.callerApp, r.resultTo,
                                new Intent(r.intent), r.resultCode,
                                r.resultData, r.resultExtras, false, false, r.userId);
                            r.resultTo = null;
                        } catch (RemoteException e) {
                            r.resultTo = null;
                        }
                    }

                    // 取消BROADCAST_TIMEOUT_MSG消息,見4.2.3
                    cancelBroadcastTimeoutLocked();

                    addBroadcastToHistoryLocked(r);
                    if (r.intent.getComponent() == null && r.intent.getPackage() == null
                            && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
                        mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
                                r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
                    }
                    mOrderedBroadcasts.remove(0);
                    r = null;
                    looped = true;
                    continue;
                }
            } while (r == null);

當Broadcasts中所有的receiver都收到了廣播或者被強制結束了廣播,都會調用performReceiveLocked;另外如果是取到BroadcastRecord第一次進來這個循環,將之前出來,因爲不滿足while (r == null)

            // 獲取下一個需要處理的Receiver的序號
            int recIdx = r.nextReceiver++;
            // 這個BroadcastRecord開始處理這個Receiver的時間
            r.receiverTime = SystemClock.uptimeMillis();
            // 如果是BroadcastRecord中第一個Receiver
            if (recIdx == 0) {
                r.dispatchTime = r.receiverTime;
                r.dispatchClockTime = System.currentTimeMillis();
            }
            if (! mPendingBroadcastTimeoutMessage) {
                long timeoutTime = r.receiverTime + mTimeoutPeriod;
                // 見4.2.4
                setBroadcastTimeoutLocked(timeoutTime);
            }
  • 獲取下一個要處理的Receiver

  • 獲取當前處理這個Receiver的時間,用於處理ANR問題,如果是當前處理的是第一個Receiver,你們dispatchTime時間和receiverTime相等,用於處理整個BroadcastRecord的超時問題

  • mPendingBroadcastTimeoutMessage的初始值是false,然後設置針對該Receiver的超時處理,超時就會造成ANR;超時的時間爲10s(前臺廣播)或者60s(後臺廣播)

            final BroadcastOptions brOptions = r.options;
            final Object nextReceiver = r.receivers.get(recIdx);
            // 如果是動態註冊的有序廣播
            if (nextReceiver instanceof BroadcastFilter) {
                BroadcastFilter filter = (BroadcastFilter)nextReceiver;
                // 發送該動態註冊的有序廣播,見4.3
                deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
                if (r.receiver == null || !r.ordered) {
                    r.state = BroadcastRecord.IDLE;
                    scheduleBroadcastsLocked();
                } else {
                    if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
                        scheduleTempWhitelistLocked(filter.owningUid,
                                brOptions.getTemporaryAppWhitelistDuration(), r);
                    }
                }
                return;
            }

判斷當前這個接收器是不是動態註冊的,如果是動態註冊的,就用deliverToRegisteredReceiverLocked發送,最後return,結束processNextBroadcast這個函數

            ResolveInfo info =
                (ResolveInfo)nextReceiver;
            ComponentName component = new ComponentName(
                    info.activityInfo.applicationInfo.packageName,
                    info.activityInfo.name);

            ......// 主要進行一些權限檢查,如果不滿足條件skip置爲true,跳過這個receiver

            if (skip) {
                r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
                r.receiver = null;
                r.curFilter = null;
                r.state = BroadcastRecord.IDLE;
                scheduleBroadcastsLocked();
                return;
            }

            r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
            r.state = BroadcastRecord.APP_RECEIVE;
            r.curComponent = component;
            r.curReceiver = info.activityInfo;

            if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
                scheduleTempWhitelistLocked(receiverUid,
                        brOptions.getTemporaryAppWhitelistDuration(), r);
            }

            // 廣播已經開始執行了,這個包不能被停止
            try {
                AppGlobals.getPackageManager().setPackageStoppedState(
                        r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
            } catch (RemoteException e) {
            } catch (IllegalArgumentException e) {
                Slog.w(TAG, "Failed trying to unstop package "
                        + r.curComponent.getPackageName() + ": " + e);
            }

            // 檢查目標進程是否已經運行
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(info.activityInfo.packageName,
                            info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
                    //將廣播發送給目標進程,見4.2.5
                    processCurBroadcastLocked(r, app);
                    return;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when sending broadcast to "
                          + r.curComponent, e);
                } catch (RuntimeException e) {
                    logBroadcastReceiverDiscardLocked(r);
                    finishReceiverLocked(r, r.resultCode, r.resultData,
                            r.resultExtras, r.resultAbort, false);
                    scheduleBroadcastsLocked();
                    // 如果啓動receiver失敗,需要重置state
                    r.state = BroadcastRecord.IDLE;
                    return;
                }
            }

            // 如果目標進程沒有啓動,則需要將他啓動,見4.2.6
            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) {
                // 這個接收器不能使用
                logBroadcastReceiverDiscardLocked(r);
                finishReceiverLocked(r, r.resultCode, r.resultData,
                        r.resultExtras, r.resultAbort, false);
                scheduleBroadcastsLocked();
                r.state = BroadcastRecord.IDLE;
                return;
            }

            mPendingBroadcast = r;
            mPendingBroadcastRecvIndex = recIdx;

處理靜態註冊下廣播的發送,如果進程已經啓動,則用processCurBroadcastLocked發送,然後返回,AMS等待廣播接收的結果,以便進行下一個receiver發送廣播,如果超時,就會觸發ANR;
如果沒有啓動,則AMS調用startProcessLocked來啓動接收者進程,啓動的時候,將當前這個BroadcastRecord放到mPendingBroadcast中保存,把receiver的序號放到mPendingBroadcastRecvIndex中保存,應用啓動完畢後會通知AMS,然後在進行廣播發送

4.2.3 cancelBroadcastTimeoutLocked 取消超時

文件:BroadcastQueue.java

    final void cancelBroadcastTimeoutLocked() {
        if (mPendingBroadcastTimeoutMessage) {
            mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
            mPendingBroadcastTimeoutMessage = false;
        }
    }

意思就是移除BROADCAST_TIMEOUT_MSG這個消息,不進行超時的處理了

4.2.4 setBroadcastTimeoutLocked 設置超時

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

由mHandler.sendMessageAtTime(msg, timeoutTime)可以看出,如果超時了就會發送消息BROADCAST_TIMEOUT_MSG

4.2.5 processCurBroadcastLocked

文件:BroadcastQueue.java

    private final void processCurBroadcastLocked(BroadcastRecord r,
            ProcessRecord app) throws RemoteException {
        if (app.thread == null) {
            throw new RemoteException();
        }
        if (app.inFullBackup) {
            skipReceiverLocked(r);
            return;
        }

        r.receiver = app.thread.asBinder();
        r.curApp = app;
        app.curReceiver = r;
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
        mService.updateLruProcessLocked(app, false, null);
        mService.updateOomAdjLocked();

        // 告訴應用啓動receiver.
        r.intent.setComponent(r.curComponent);

        boolean started = false;
        try {
            mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
                                      PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
            // 見4.4處理靜態廣播
            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;
            }
        }
    }

這裏最重要的是scheduleReceiver

4.2.6 啓動進程後通知AMS

流程如下:

  • ActivityThread.Main

  • ActivityThread.attach

  • AMS. attachApplication

  • AMS. attachApplicationLocked

到了attachApplicationLocked之後裏面一行這樣的代碼:

didSomething |= sendPendingBroadcastsLocked(app);

我們繼續看看sendPendingBroadcastsLocked

    boolean sendPendingBroadcastsLocked(ProcessRecord app) {
        boolean didSomething = false;
        for (BroadcastQueue queue : mBroadcastQueues) {
            didSomething |= queue.sendPendingBroadcastsLocked(app);
        }
        return didSomething;
    }

主要是這行didSomething |= queue.sendPendingBroadcastsLocked(app),我們繼續追

    public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
        boolean didSomething = false;
        final BroadcastRecord br = mPendingBroadcast;
        if (br != null && br.curApp.pid == app.pid) {
            if (br.curApp != app) {
                return false;
            }
            try {
                mPendingBroadcast = null;
                // 發送廣播,見4.2.5
                processCurBroadcastLocked(br, app);
                didSomething = true;
            } catch (Exception e) {
                logBroadcastReceiverDiscardLocked(br);
                finishReceiverLocked(br, br.resultCode, br.resultData,
                        br.resultExtras, br.resultAbort, false);
                scheduleBroadcastsLocked();
                // We need to reset the state if we failed to start the receiver.
                br.state = BroadcastRecord.IDLE;
                throw new RuntimeException(e.getMessage());
            }
        }
        return didSomething;
    }

從mPendingBroadcast取出正在等待的BroadcastRecord,然後將mPendingBroadcast置爲null,接着用processCurBroadcastLocked發送廣播

4.3 deliverToRegisteredReceiverLocked 用來發送所有動態註冊的廣播

文件:BroadcastQueue.java

    private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered, int index) {
        boolean skip = false;
        if (filter.requiredPermission != null) {
            int perm = mService.checkComponentPermission(filter.requiredPermission,
                    r.callingPid, r.callingUid, -1, true);
            // 如果接收者沒有權限,skip爲true
            if (perm != PackageManager.PERMISSION_GRANTED) {
                skip = true;
            }
            ......
        }

        ...... //中間這很長一大串代碼都是用來檢查權限,如果不符合要求skip會置爲true

        if (skip) {
            // 將狀態改爲DELIVERY_SKIPPED
            r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
            return;
        }

        // 複查一下權限
        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
            if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
                    filter.owningUserId)) {
                r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
                return;
            }
        }

        r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;

        // 如果是有序的動態廣播
        if (ordered) {
            r.receiver = filter.receiverList.receiver.asBinder();
            r.curFilter = filter;
            filter.receiverList.curBroadcast = r;
            r.state = BroadcastRecord.CALL_IN_RECEIVE;
            if (filter.receiverList.app != null) {
                r.curApp = filter.receiverList.app;
                filter.receiverList.app.curReceiver = r;
                mService.updateOomAdjLocked(r.curApp);
            }
        }
        try {
            if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
                if (ordered) {
                    skipReceiverLocked(r);
                }
            } else {
                // 發送動態註冊的廣播,見4.3.1
                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
            }
            if (ordered) {
                r.state = BroadcastRecord.CALL_DONE_RECEIVE;
            }
        } 
        ......
    }
  • 進來時首先檢查權限,不滿足的將skip設置爲true,並將對應的receiver的狀態設置爲DELIVERY_DELIVERED

  • 如果是有序的動態廣播,以一種同步的方式發送廣播,並且在BroadcastRecord中保存receiver,curFilter和state,其中state爲CALL_IN_RECEIVE

  • 通過performReceiveLocked發送動態註冊的廣播

  • 如果有序的動態廣播,state改爲CALL_DONE_RECEIVE

4.3.1 performReceiveLocked

文件:BroadcastQueue.java

    void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        // 進程已經啓動的情況,用binder異步機制向receiver發送數據
        if (app != null) {
            if (app.thread != null) {
                try {
                    // 見4.3.2
                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.repProcState);
                } catch (RemoteException ex) {
                    synchronized (mService) {
                        app.scheduleCrash("can't deliver broadcast");
                    }
                    throw ex;
                }
            } else {
                // 應用已經死亡,Receiver不存在
                throw new RemoteException("app.thread must not be null");
            }
        } else {
            // 調用進程爲null時,則走這個分支
            receiver.performReceive(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
        }
    }

這裏分爲兩種情況:

  • 調用進程和app.thread都不爲null,這時通過binder方式向註冊進程發起調用,其中Binder調用裏會加上IBinder.FLAG_ONEWAY標記
    調用流程爲:

  • 調用進程爲null,這時執行receiver.performReceive(intent, resultCode, data, extras, ordered,sticky, sendingUser);

其中app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,data, extras, ordered, sticky, sendingUser, app.repProcState)的調用流程爲:

  • ApplicationThreadNative.ApplicationThreadProxy. scheduleRegisteredReceiver(system_server進程)

  • Binder驅動(Binder驅動進程,ONEWAY)

  • ApplicationThreadNative. scheduleRegisteredReceiver(應用進程,Binder線程向主線程發送消息)

  • Binder驅動返回 (Binder驅動進程)

  • ActivityThread. scheduleRegisteredReceiver(應用進程,處理消息)

4.3.2 scheduleRegisteredReceiver

        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);
            // 見4.3.3
            receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                    sticky, sendingUser);
        }

4.3.3 performReceive

這是LoadedApk.ReceiverDispatcher.InnerReceiver.performReceive

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

                ......

                if (rd != null) {
                    // 見4.3.4
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                } 

                ......

            }

4.3.4 ReceiverDispatcher.performReceive

文件:LoadedApk.java

        public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
            // 見4.3.5
            final Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
            if (intent == null) {
                Log.wtf(TAG, "Null intent received");
            } else {
                if (ActivityThread.DEBUG_BROADCAST) {
                    int seq = intent.getIntExtra("seq", -1);
                    Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction()
                            + " seq=" + seq + " to " + mReceiver);
                }
            }
            // 將廣播信息放到args中,post到主線程中執行,見4.3.5
            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);
                    // 告訴AMS,廣播處理完畢,動態的有序廣播會走這裏見4.3.6
                    args.sendFinished(mgr);
                }
            }
        }

4.3.5 Args

final class Args extends BroadcastReceiver.PendingResult implements Runnable {
            private Intent mCurIntent;
            private final boolean mOrdered;
            private boolean mDispatched;

            public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                    boolean ordered, boolean sticky, int sendingUser) {
                super(resultCode, resultData, resultExtras,
                        mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
                        sticky, mIIntentReceiver.asBinder(), sendingUser, intent.getFlags());
                mCurIntent = intent;
                mOrdered = ordered;
            }

            public void run() {
                final BroadcastReceiver receiver = mReceiver;
                final boolean ordered = mOrdered;

                final IActivityManager mgr = ActivityManagerNative.getDefault();
                final Intent intent = mCurIntent;
                if (intent == null) {
                    Log.wtf(TAG, "Null intent being dispatched, mDispatched=" + mDispatched);
                }

                mCurIntent = null;
                mDispatched = true;
                if (receiver == null || intent == null || mForgotten) {
                    if (mRegistered && ordered) {
                        sendFinished(mgr);
                    }
                    return;
                }

                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveReg");
                try {
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    intent.prepareToEnterProcess();
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    // 調用接收者BroadcastReceiver.onReceive()
                    receiver.onReceive(mContext, intent);
                } catch (Exception e) {
                    if (mRegistered && ordered) {
                        sendFinished(mgr);
                    }
                    if (mInstrumentation == null ||
                            !mInstrumentation.onException(mReceiver, e)) {
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                        throw new RuntimeException(
                            "Error receiving broadcast " + intent
                            + " in " + mReceiver, e);
                    }
                }

                if (receiver.getPendingResult() != null) {
                    // 見4.3.6
                    finish();
                }
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
        }

這裏主要調用的是receiver.onReceive(mContext, intent),至此非串行的動態註冊的廣播已經接收完畢,值得一提的是下面那個finish()函數,作用是結束這個廣播,並將結果發送給下一個廣播

4.3.6 sendFinished

文件:BroadcastReceiver.java

        public void sendFinished(IActivityManager am) {
            synchronized (this) {
                if (mFinished) {
                    throw new IllegalStateException("Broadcast already finished");
                }
                mFinished = true;

                try {
                    if (mResultExtras != null) {
                        mResultExtras.setAllowFds(false);
                    }
                    // 是否是有序的
                    if (mOrderedHint) {
                        // 見4.3.7
                        am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                                mAbortBroadcast, mFlags);
                    } else {
                        // 什麼都不幹,目的是告訴ActivityManager,這個廣播我處理我完了
                        am.finishReceiver(mToken, 0, null, null, false, mFlags);
                    }
                } catch (RemoteException ex) {
                }
            }
        }

通過前面的分析,我們知道,動態廣播如果是無序,在執行finish的時候,因爲mType等於TYPE_REGISTERED,ordered爲false,所以什麼都不執行;只有有序的動態廣播最終調用sendFinished,走mOrderedHint爲true的那條線路,這裏面mAbortBroadcast值得注意,如果爲true,級別高的接收器收到廣播後,級別低的就收不到廣播了,好了,我們來看看finishReceiver,看這裏函數你能知道動態的廣播爲什麼可以循環起來

4.3.7 finishReceiver

文件:ActivityManagerService.java

    public void finishReceiver(IBinder who, int resultCode, String resultData,
            Bundle resultExtras, boolean resultAbort, int flags) {

        // Refuse possible leaked file descriptors
        if (resultExtras != null && resultExtras.hasFileDescriptors()) {
            throw new IllegalArgumentException("File descriptors passed in Bundle");
        }

        final long origId = Binder.clearCallingIdentity();
        try {
            boolean doNext = false;
            BroadcastRecord r;

            synchronized(this) {
                BroadcastQueue queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
                        ? mFgBroadcastQueue : mBgBroadcastQueue;
                r = queue.getMatchingOrderedReceiver(who);
                if (r != null) {
                    doNext = r.queue.finishReceiverLocked(r, resultCode,
                        resultData, resultExtras, resultAbort, true);
                }
            }

            if (doNext) {
                // 處理下一條廣播
                r.queue.processNextBroadcast(false);
            }
            trimApplications();
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

首先判斷是不是是前臺廣播還是後臺廣播,如果是後臺廣播則開始執行下一條廣播,值得一提的是resultAbort和mAbortBroadcast上面的一致,最終的影響是這行代碼:

if (r.receivers == null || r.nextReceiver >= numReceivers
                        || r.resultAbort || forceReceive) {

4.4 scheduleReceiver處理靜態廣播

我們還是一步步來看,首先:
文件: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;
            // 見4.4.1
            sendMessage(H.RECEIVER, r);
        }

4.4.1 H

文件:ActivityThread.java

        public void handleMessage(Message msg) {
            switch (msg.what) {
            ......
                case RECEIVER:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp");
                    // 見4.4.2
                    handleReceiver((ReceiverData)msg.obj);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
             ......

4.4.2 handleReceiver

文件:ActivityThread.java

    private void handleReceiver(ReceiverData data) {
        unscheduleGcIdler();

        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);
            // 用反射的方式生成BroadcastReceiver
            receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
        } catch (Exception e) {
            data.sendFinished(mgr);
            throw new RuntimeException(
                "Unable to instantiate receiver " + component
                + ": " + e.toString(), e);
        }

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

            ContextImpl context = (ContextImpl)app.getBaseContext();
            sCurrentBroadcastIntent.set(data.intent);
            receiver.setPendingResult(data);
            // 調用目標Receiver的onReceive()
            receiver.onReceive(context.getReceiverRestrictedContext(),
                    data.intent);
        } catch (Exception e) {
            data.sendFinished(mgr);
            if (!mInstrumentation.onException(receiver, e)) {
                throw new RuntimeException(
                    "Unable to start receiver " + component
                    + ": " + e.toString(), e);
            }
        } finally {
            sCurrentBroadcastIntent.set(null);
        }

        if (receiver.getPendingResult() != null) {
            data.finish();
        }
    }

用反射的方法獲得類,然後生成BroadcastReceiver,接着調用onReceive()方法

5 總結與建議

5.1 並行廣播和串行廣播的發送特點

並行發送廣播
這是發廣播給動態註冊接收器時的特點,採用異步的形式,廣播之間不會互相干擾

串行發送廣播
這是採用串行發送廣播的方式,採用的是同步的機制,根據優先級來發送,優先級高的receiver可以停止廣播想優先級低的發送;靜態註冊的Receiver和有序的動態註冊的Receiver均採用這個方式

5.2 廣播處理的流程圖

廣播處理流程圖
這張圖展示了有序和無序廣播的處理流程

5.3 廣播處理的時序圖

廣播處理時序圖

5.4 建議

  • 判斷自己的所需要獲取的廣播是否能夠動態註冊獲取,因爲Google基於優化考慮,比如SCREEN_OFF或SCREEN_OFF已經不能通過靜態註冊來獲取了

  • 不要在onReceiver中進行耗時的操作,如果必須要這樣做,可以發送Intent啓動一個service來做

  • 如果需要一直對廣播保持監聽,建議使用靜態註冊,因爲靜態註冊就算是目標進程不在了也可以重啓,而動態註冊的Receiver的生命週期會受應用的影響,應用銷燬了,Receiver也就收不到廣播了

  • 如果只在同一APP內部發送和接收廣播,將exported屬性設置成false,或者可以使用LocalBroadcastManager類(本地廣播)

  • 廣播發送和接收建議帶上權限

  • 如果在onReceive()中開了一個子線程來執行工作,會碰上onReceive()已經返回,而子線程還沒有做完工作的情況,這時候如果AMS結束了進程,這時子線程也會掛掉,但是工作卻沒有做完,可以利用要PendingResult來規避這個問題,一般finish()方法一般都是PendingResult不等於null的時候調用(參考4.3.6),使用方法如下:

public void onReceive(final Context context, final Intent intent) {
        //得到PendingResult
        final PendingResult result = goAsync();  

        //放到異步線程中執行
        AsyncHandler.post(new Runnable() {  
            @Override  
            public void run() {  
                handleIntent(context, intent);//可進行一些耗時操作  
                result.finish();  
            }  
        });  
    }

一旦調用goAsync(),那麼BroadcastReceiver中的PendingResult將爲null,這時finish方法得不到執行,AMS就收不到onReceive()執行完的消息,一直要到子線程調用result.finish()纔會知道,這個目的只是爲了不阻塞主線程,操作還是要在規定時間內完成

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章