四大組件之BroadcastReceiver源碼解析

BroadcastReceiver

一 BroadcastReceiver 簡(jiǎn)介

BroadcastReceiver 是一種消息型組件,稱之為廣播,用于在不同的組件乃至不同的應(yīng)用之間傳遞消息。
廣播的注冊(cè)分為兩種方式:

  • 靜態(tài)注冊(cè)
    靜態(tài)注冊(cè)是指在 AndroidManiifest中注冊(cè)廣播,這種廣播在應(yīng)用安裝時(shí)會(huì)被系統(tǒng)解析、不需要應(yīng)用啟動(dòng)就可以收到相應(yīng)的廣播.
  • 動(dòng)態(tài)注冊(cè)。
    動(dòng)態(tài)注冊(cè)廣播需要通過(guò)Context.registrerReceiver() 來(lái)實(shí)現(xiàn),并且在不需要的時(shí)候要通過(guò)Context.unregistrerReceiver() 來(lái)解除廣播,此種形態(tài)的廣播必須要應(yīng)用啟動(dòng)才能注冊(cè)并接收廣播。

廣播收發(fā)器組件可以用來(lái)實(shí)現(xiàn)低耦合的觀察者模式,觀察者和被觀察者之間可以沒(méi)有任何耦合,由于BroadcastReceiver的特性,它不適合用來(lái)執(zhí)行耗時(shí)操作.BroadcastReceiver一般來(lái)說(shuō)不需要停止,它也沒(méi)有停止的概念。

二 BroadcastReceiver 的使用

我們回顧一下廣播的使用方法,首先要冊(cè)
定義廣播接收者,只需要繼承 BroadcastReceiver并重寫 on Receive方法即可,下面是一個(gè)典型的廣播接收者的實(shí)現(xiàn):

public class MyReceiver extends BroadcastReceiver {
   @Override
   public void onReceive(Context context, Intent intent) {
   // onReceive函數(shù)不能做耗時(shí)的事情,參考值:10s以內(nèi)
      String action= intent. getAction ();
   // do something
   }
}

定義好了廣播接收者,接著還需要注冊(cè)廣播接收者,注冊(cè)分為兩種方式,既可以在AndroidManifest文件中靜態(tài)注冊(cè),也可以通過(guò)代碼動(dòng)態(tài)注冊(cè)。

靜態(tài)注冊(cè)的示例如下

        <receiver android:name=".MyReceiver">
            <intent-filter>
                <action android:name="com.lsh.receiver.LAUNCH"/>
            </intent-filter>
        </receiver>

通過(guò)代碼來(lái)動(dòng)態(tài)注冊(cè)廣播也是很簡(jiǎn)單的,如下所示。需要注意的是,動(dòng)態(tài)注冊(cè)
需要在合適的時(shí)機(jī)進(jìn)行解注冊(cè),解注冊(cè)采用 unregisterReceiver方法。

IntentFilter filter = new IntentFilter();       
filter.addAction("com.lsh.receiver.LAUNCH");
registerReceiver(new MyReceiver(), filter);

前面兩步都完成了以后,就可以通過(guò)send方法來(lái)發(fā)送廣播了,如下所示。

Intent intent= new Intent()
intent.setAction("com.lsh.receiver.LAUNCH");
sendBroadcast(intent);

三 廣播的注冊(cè)過(guò)程

廣播的注冊(cè)分為靜態(tài)注冊(cè)和動(dòng)態(tài)注冊(cè),其中靜態(tài)注冊(cè)的廣播在應(yīng)用安裝時(shí)由系統(tǒng)自動(dòng)完成注冊(cè),具體來(lái)說(shuō)是由PMS( PackageManager Service來(lái)完成整個(gè)注冊(cè)過(guò)程的,除了廣播以外,其他三大組件也都是在應(yīng)用安裝時(shí)由PMS解析并注冊(cè)的。這里只分析廣播的動(dòng)態(tài)注冊(cè)的過(guò)程,動(dòng)態(tài)注冊(cè)的過(guò)程是從 ContextWrapper 的 registerReceiver方法開(kāi)始的,和 Activity以及 Service一樣。 ContextWrapper并沒(méi)有做實(shí)際的工作,而是將注冊(cè)過(guò)程直接交給了Contextlmpl(這個(gè)文件是保護(hù)文件,就是注解了是內(nèi)部保護(hù)文件,所以無(wú)法找到,可以去SDk的安裝目錄中的sources文件夾中直接這個(gè)Java文件)完成,如下所

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

ContextImpl的 registerReceiver方法調(diào)用了自己的 registerReceiverInternal方法,它的實(shí)現(xiàn)如下所示:

 private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
        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 = ActivityManager.getService().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                    broadcastPermission, userId, flags);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

在上面的代碼中,系統(tǒng)首先從 mPackagelnfo獲取 IIntentReceiver對(duì)象,然后再采用跨進(jìn)程的方式向AMS發(fā)送廣播注冊(cè)的請(qǐng)求。之所以采用 IIntentReceiver而不是直接采用BroadcastReceiver,這是因?yàn)樯鲜鲎?cè)過(guò)程是一個(gè)進(jìn)程間通信的過(guò)程,而 BroadcastReceiver作為 Android的一個(gè)組件是不能直接跨進(jìn)程傳遞的,所以需要通過(guò) IIntentReceiver來(lái)中轉(zhuǎn)下。毫無(wú)疑問(wèn), IIntentReceiver必須是一個(gè) Binder接口,它的具體實(shí)現(xiàn)是 LoadedApk Receiver Dispatcher. InnerReceiver, Receiver Dispatcher的內(nèi)部同時(shí)保存了 BroadcastReceiverInner receiver,這樣當(dāng)接收到廣播時(shí), ReceiverDispatcher可以很方便地調(diào)用BroadcastReceiveron Receive方法??梢园l(fā)現(xiàn), BroadcastReceiver的這個(gè)過(guò)程和 Service的實(shí)現(xiàn)原理類似;

下面看一下 ReceiverDispatchergetlIntentReceiver的實(shí)現(xiàn), 如下所示。很顯然, getReceiverDispatcher方法重新創(chuàng)建了一個(gè) Receiver Dispatcher對(duì)象并,將其保存的 InnerReceiver對(duì)象作為返回值返回,其中 Inner Receiver對(duì)象和 BroadcastReceiver都是在 ReceiverDispatcher的構(gòu)造方法中被保存起來(lái)的。

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

注冊(cè)廣播的真正實(shí)現(xiàn)是在 AMS 中,AMS的 registerReceiver 中,最終會(huì)把遠(yuǎn)程的 InnerReceiver 對(duì)象以及 IntentFilter 對(duì)象存儲(chǔ)起來(lái),這種整個(gè)廣播的注冊(cè)過(guò)程就完成了,如下代碼:

 public Intent reiisterReceiver(IApplicationThread caller, String callerPackage, 
                                   IIntentReceiver receiver, IntentFilter filter, String permission,
                                   int userId) {
        ...
        mRegisterReceivers.put(receiver.asBinder(), rl);      
        BroadcastFilter broadcastFilter = new BroadcastFilter(filter, rl, callerPackage, permission, 
                callingUid, userId);
        rl.add(bf);
        mRegisterResolver.addFilter(bf);
        ...
    }

四 廣播的發(fā)送和接受過(guò)程

廣播的發(fā)送和接收,其本質(zhì)是一個(gè)過(guò)程的兩個(gè)階段。這里從廣播的發(fā)送可以說(shuō)起,廣播的發(fā)送仍然開(kāi)始于 Context WrappersendBroadcast方法,之所以不是 Context,那是因?yàn)?Contextsend Broadcast是一個(gè)抽象方法。和廣播的注冊(cè)過(guò)程一樣, Context Wrappersend Broadcast方法仍然什么都不做,只是把事情交給 ContextImpl去處理:

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

從上邊看 ContextImpl 直接向 AMS 發(fā)起了一個(gè)異步請(qǐng)求用于發(fā)送廣播。

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

broadcastIntent 調(diào)用了 broadcastIntentLocked 方法,AMS 的broadcastIntentLocked,在此方法中加了了

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

表示在 Android5.1 中 默認(rèn)情況下廣播不會(huì)發(fā)送給已停止的應(yīng)用了。

在BroadcastIntentLockes的內(nèi)部,會(huì)根據(jù)意圖intent-Fl1ter查找出匹配的廣播接收者并 經(jīng)過(guò)一系列的條件過(guò)濾,最終會(huì)將滿足條件的廣播接收者添加到Broadcast Queue中,接著B(niǎo)roadcast Queue就會(huì)將廣播發(fā)送給相應(yīng)的廣播接收者:

 if ((receivers != null && receivers.size() > 0)
                || resultTo != null) {
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, callerInstantApp, 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());

            final BroadcastRecord oldRecord =
                    replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
            if (oldRecord != null) {
                // Replaced, fire the result-to receiver.
                if (oldRecord.resultTo != null) {
                    final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
                    try {
                        oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
                                oldRecord.intent,
                                Activity.RESULT_CANCELED, null, null,
                                false, false, oldRecord.userId);
                    } catch (RemoteException e) {
                        Slog.w(TAG, "Failure ["
                                + queue.mQueueName + "] sending broadcast result of "
                                + intent, e);

                    }
                }
            } else {
                queue.enqueueOrderedBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
        }

下邊看一下 BroadcastQueue 中廣播的發(fā)送過(guò)程實(shí)現(xiàn),如下

 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的scheduleBroadcastsLocked 方法并沒(méi)有立即發(fā)送廣播,而是發(fā)送了一個(gè) BROADCAST_INTENT_MSG ,BroadcastQueue收到消息后會(huì)調(diào)用 process-NextBroadcast 方法,BroadcastQueue 的 processNextBroadcast 方法對(duì)普通廣播處理如下:

 // First, deliver any non-serialized broadcasts right away.
            while (mParallelBroadcasts.size() > 0) {
                r = mParallelBroadcasts.remove(0);
                r.dispatchTime = SystemClock.uptimeMillis();
                r.dispatchClockTime = System.currentTimeMillis();

                if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                    Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
                        System.identityHashCode(r));
                    Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                        createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
                        System.identityHashCode(r));
                }

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

可以看到,無(wú)序廣播存儲(chǔ)在mParallelBroadcasts中,系統(tǒng)會(huì)遍歷mParallelBroadcasts并將其中的廣播發(fā)送給它們所有的接收者,具體的發(fā)送過(guò)程是通過(guò)delivetToRegisteredReceiveLoack 方法來(lái)實(shí)現(xiàn)的。delivetToRegisteredReceiveLoack方法負(fù)責(zé)將一個(gè)廣播發(fā)送給一個(gè)特定的接收者,它內(nèi)部調(diào)用了performReceiveLocked來(lái)完成具體的發(fā)送過(guò)程;

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

該方法實(shí)現(xiàn)如下

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

ApplicationThreadscheduleRegisterReceiver 的實(shí)現(xiàn)比較簡(jiǎn)單,他是通過(guò) InnerReceiver 來(lái)實(shí)現(xiàn)廣播的接收。

 void scheduleRegisterReceiver(IIntentReceiver receiver, Intent intent,
                                  int resultCode, String dataStr, Bundle extras,
                                  boolean ordered, boolean sticky, int sendingUser,
                                  int processState) {
        updateProcessState(processState, false);
        receiver.performReceiver(intent, resultCode, dataStr, extras, ordered, sticky, sendingUser);
    }

InnerReceiver的 performReceiver方法會(huì)調(diào)用LoadApk.ReceiverDispatch的 perform-Receiver 方法

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.getRunnable())) {
                if (mRegistered && ordered) {
                    IActivityManager mgr = ActivityManager.getService();
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing sync broadcast to " + mReceiver);
                    args.sendFinished(mgr);
                }
            }
        }

    }

創(chuàng)建了一個(gè) Args 對(duì)象并通過(guò) mAcvitityThread 的 post 方法來(lái)執(zhí)行 Args 中的邏輯,
而args實(shí)現(xiàn)了Runnable接口.mActivityThread是一個(gè)Handler,它其實(shí)就是ActivityThread中的mh,mh的類型是ActivityThread的內(nèi)部類Н,關(guān)于H這個(gè)類前面已經(jīng)介紹過(guò)了,這里就不再多說(shuō)了.在args的方法中有如下幾行代碼:


很顯然,這個(gè)時(shí)候BroadcastReceiver的onReceiver方法被執(zhí)行了,也就是說(shuō)應(yīng)用已經(jīng)接收到廣播了,同時(shí) onReceiver 方法是在廣播接收者的主線程中被調(diào)用的.到這里,整個(gè)廣播的注冊(cè)、發(fā)送和接收過(guò)程已經(jīng)講解完畢

參考資料:
《Android開(kāi)發(fā)藝術(shù)探索》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容