BroadcastReceiver 工作過程

說到廣播的使用,就不得不提它的兩種註冊方式,動態註冊和靜態註冊。靜態註冊在程序啓動加載manifest文件時候完成。因此這裏就以動態註冊來看一下BroadcastReceiver 的工作過程。

註冊廣播

BroadcastReceiver 是一個抽象的類,因此我們需要繼承BroadcastReceiver 實現一個我們自己的廣播類

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

然後在通過registerReceiver註冊廣播

        mMyReceiver = new MyReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.intent.action.ZY_TEST");
        registerReceiver(mMyReceiver, filter);

這裏面我們就已經註冊了一個action爲"android.intent.action.ZY_TEST"的廣播。

1、registerReceiver()
    @Override
    public Intent registerReceiver(
        BroadcastReceiver receiver, IntentFilter filter) {
        return mBase.registerReceiver(receiver, filter);
    }

在前面兩篇我們已經mBase的實現類是ContextImpl。所以查看ContextImpl的registerReceiver()。而ContextImpl.registerReceiver()又會執行ContextImpl.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) {
                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 {
            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();
        }
    }

在registerReceiverInternal()主要做了兩件事。

1、構造一個LoadedApk.ReceiverDispatcher對象,並獲得通過其getIIntentReceiver()獲得IIntentReceiver對象。

和Service的ServiceDispatcher作用是一樣的,爲BroadcastReceiver賦予跨進程通信的能力。

廣播的本身並不具備跨進程通信的功能,所以需要通過Binder機制來實現跨進程通信。而ReceiverDispatcher的作用就是創建一個繼承IIntentReceiver.Stub的InnerReceiver類,並保存InnerReceiver和BroadcastReceiver對象。

InnerReceiver相當於Binder機制的服務端,收到消息後,會通過ReceiverDispatcher對象來講消息調度分發給BroadcastReceiver。

2、調用AMS的registerReceiver方法。這裏同樣使用Binder機制來完成registerReceiver()的調用

2、ActivityManagerService & registerReceiver()

廣播會在SMS的registerReceiver完成註冊。registerReceiver代碼還是挺長的。這裏我們只看一下注冊部分的代碼:

			//從集合中獲得註冊廣播接收器的集合類
            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);

可以看到通過將Binder對象和ReceiverList對象以鍵值對的形式存儲到HashMap的集合mRegisteredReceivers中,完成註冊。
在這裏插入圖片描述
ReceiverList是一個廣播接收器的集合類,因爲在註冊的時候我們可以一次註冊多個廣播。

發送廣播

發送廣播也分爲普通廣播,優先級廣播和粘性廣播。這裏以普通廣播爲例。

        Intent intent = new Intent();
        intent.setAction("android.intent.action.ZY_TEST");
        intent.putExtra("msg","你好");
        sendBroadcast(intent);

sendBroadcast同樣會調用ContextImpl的sendBroadcast方法

1、ContextImpl & sendBroadcast
        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();
        }

繼續執行AMS的broadcastIntent方法。broadcastIntent又會執行broadcastIntentLocked方法

2、AMS & broadcastIntentLocked
    final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
        intent = new Intent(intent);

        // By default broadcasts do not go to stopped apps.
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
        ...
        // Verify that protected broadcasts are only being sent by system code,
        // and that system code is only sending protected broadcasts.
        final String action = intent.getAction();
		
		...
            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);
                    
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
       ...

broadcastIntentLocked方法很長,首先會給廣播添加一個默認的flag:不進入停止的應用內。

然後會校驗發送廣播的權限,因爲有些事系統廣播。

通過intent來查找註冊的廣播,執行 queue.scheduleBroadcastsLocked();

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

在handler接收消息的地方會執行processNextBroadcast方法:

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

可以看到,如果是普通廣播,則會立即遍歷發送。mParallelBroadcasts就是我們在第一步註冊廣播時,存儲廣播的HashMap。

然後調用deliverToRegisteredReceiverLocked方法,在該方法中會執行performReceiveLocked()

BroadcastQueue & performReceiveLocked

    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);
                // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
                // DeadObjectException when the process isn't actually dead.
                //} catch (DeadObjectException ex) {
                // Failed to call into the process.  It's dying so just let it die and move on.
                //    throw ex;
                } catch (RemoteException ex) {
                    // Failed to call into the process. It's either dying or wedged. Kill it gently.
                    synchronized (mService) {
                        Slog.w(TAG, "Can't deliver broadcast to " + app.processName
                                + " (pid " + app.pid + "). Crashing it.");
                        app.scheduleCrash("can't deliver broadcast");
                    }
                    throw 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);
        }
    }

下面則執行app.thread的scheduleRegisteredReceiver方法。app.thread也就是ApplicationThread。

        // This function exists to make sure all receiver dispatching is
        // correctly ordered, since these are one-way calls and the binder driver
        // applies transaction ordering per object for such calls.
        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);
        }

然後調用receiver.performReceive()。這裏的receiver也就是InnerReceiver。

而InnerReceiver的performReceive方法又會調用ReceiverDispatcher的performReceive方法。

      final Handler mActivityThread;
        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 (ActivityThread.DEBUG_BROADCAST) {
                    int seq = intent.getIntExtra("seq", -1);
                    Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction()
                            + " seq=" + seq + " to " + mReceiver);
                }
            }
            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);
                }
            }
        }

    }

然後通過mActivityThread.post(args)執行Args 對象中的run方法。

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

                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveReg");
                try {
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    intent.prepareToEnterProcess();
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    receiver.onReceive(mContext, intent);
                } catch (Exception e) {
					...
                }
                
                if (receiver.getPendingResult() != null) {
                    finish();
                }
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }

在run方法中會執行receiver.onReceive(mContext, intent); 接收廣播消息

總結

其它三大組件工作流程:

ContentProvider使用

Activity的啓動流程

Service啓動流程

參考:開發藝術探索

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