1. 程式人生 > >BroadcastReceiver註冊、傳送、接收原始碼分析

BroadcastReceiver註冊、傳送、接收原始碼分析

BroadcastReceiver包括兩方面,一是廣播的註冊過程,二是廣播的傳送和接收過程。

首先要定義一個廣播的接受者,只需要整合BroadcastReceiver,並重寫它的onReceiver()方法就可以了,在該方法內不要做超過10ms的耗時工作。

註冊廣播接受者有兩種方式;

一是在AndroidManifest檔案中靜態註冊:

二是在程式碼中動態註冊,採用registerReceiver註冊,但它需要在合適的時機進行解除註冊,採用unregisterReceiver。

IntentFilter filter =new IntentFilter();

filter.addAction(“….”);

registerReceiver(new MyReceiver(),filter);

常用在activity的onDestroy()方法中解除註解unregisterReceiver();

然後是傳送廣播:

Intent intent = new Intent();

intent.setAction(“…)

sendBroadcast(intent);

先附上一張廣播動態註冊、傳送、接收的流程圖


1.廣播的註冊過程

廣播的註冊分為靜態和動態註冊兩種,靜態註冊的廣播是在應用安裝時有系統自動完成註冊,具體來說是有PMS(PackageManagerService)來解析註冊的,除了動態註冊的廣播,其他三大元件也是應用在安裝時由PMS解析並註冊。這裡分析動態廣播的註冊過程。動態註冊是從ContextWrapper的registerReceiver開始,ContextWrapper並沒有做實際的工作,而是將註冊過程交由ContextImpl來完成:

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

ContextImpl的registerReceiver呼叫了registerReceiverInternal。
 @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()); } 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(); } }

系統通過mPackageInfo獲取IIntentReceiver,然後採用跨程序的方式向AMS傳送註冊請求。BroadcastReceiver作為android元件是不能跨程序通訊的,需要通過IIntentReceiver中轉。IIntentReceiver是一個Binder介面,具體實現是有LoadedApk.ReceiverDispatcher.InnerReceiver,它的內部同時保留了BroadcastReceiver和InnerReceiver,這裡的邏輯類似於binService的實現原理,後面會具體說。檢視getReceiverDispatcher方法:

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

  final IIntentReceiver.Stub mIIntentReceiver;
IIntentReceiver getIIntentReceiver() {
            return mIIntentReceiver;
        }


        ReceiverDispatcher(BroadcastReceiver receiver, Context context,
                Handler activityThread, Instrumentation instrumentation,
                boolean registered) {
            if (activityThread == null) {
                throw new NullPointerException("Handler must not be null");
            }

            mIIntentReceiver = new InnerReceiver(this, !registered);
            mReceiver = receiver;
            mContext = context;
            mActivityThread = activityThread;
            mInstrumentation = instrumentation;
            mRegistered = registered;
            mLocation = new IntentReceiverLeaked(null);
            mLocation.fillInStackTrace();
        }

 final static class InnerReceiver extends IIntentReceiver.Stub {
            final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
            final LoadedApk.ReceiverDispatcher mStrongRef;
	InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
                mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
                mStrongRef = strong ? rd : null;
            }

            @Override
            public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
	……
	if (rd != null) {
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                }
	……
}

顯然getReceiverDispatcher方法重建了一個ReceiverDispatcher物件,並將其儲存的InnerReceiver物件作為返回值返回。檢視AMS的registerReceiver的實現,下面的方法只需注意關鍵的一點,最終把InnerReceiver物件以及IntentFilter物件儲存起來,這樣就完成廣播的註冊。

final class ReceiverList extends ArrayList<BroadcastFilter>
        implements IBinder.DeathRecipient 
  /**
     * Keeps track of all IIntentReceivers that have been registered for broadcasts.
     * Hash keys are the receiver IBinder, hash value is a ReceiverList.
     */
    final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();

 public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
        enforceNotIsolatedCaller(“registerReceiver");
      …….
 ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
	…….
 mRegisteredReceivers.put(receiver.asBinder(), rl);
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId);
            rl.add(bf);
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadcast");
            }
            mReceiverResolver.addFilter(bf);
	…….
}

2.廣播的傳送和接收過程

通過send方法傳送廣播,AMS會查出匹配的廣播接收並處理。廣播的傳送種類有普通廣播、有序廣播、粘性廣播。

普通廣播:廣播發出之後所有滿足條件的應用都能獲取到廣播裡面的資料,缺點是應用獲取廣播中的資料修改之後不能傳遞給其它接收廣播的應用。broadcast,

sendBroadcast():傳送給所有廣播接收者,按照註冊的先後順序,如果你這個時候設定了廣播接收者的優先順序,優先順序如果恰好與註冊順序相同,則不會有任何問題,如果順序不一樣,會出leaked IntentReceiver 這樣的異常,並且在前面的廣播接收者不能呼叫abortBroadcast()方法將其終止,如果呼叫會出BroadcastReceiver trying to return result during a non-ordered broadcast的異常,當然,先接收到廣播的receiver可以修改廣播資料。

有序廣播:orderbroadcast,廣播發出之後各應用根據應用的優先順序依次接收廣播,優先順序高的應用接收廣播之後修改的資料也可以傳遞給後來的接受者,優先順序高的應用也可以呼叫abrotbroascast方法停止該廣播的向下傳播,優先順序靠應用的android:prioioty屬性控制,該值的取值區間為-1000到1000,值越大優先順序越高。

sendOrderedBroadcast()按照設定的優先順序傳送,還有這個api能指定final的receiver,這個receiver是最後一個接收廣播時間的receiver,並且一定會接收到廣播事件,是不能被前面的receiver攔截的。實際做實驗的情況是這樣的,假設我有3個receiver依序排列,並且sendOrderedBroadcast()方法指定了一個finalReceiver,那麼intent傳遞給這4個Receiver的順序為Receiver1-->finalReceiver-->Receiver2-->finalReceiver-->Receiver3-->finalReceiver。這個特性可以用來統計系統中能監聽某種廣播的Receiver的數目。

粘性廣播:使用這個api需要許可權android.Manifest.permission.BROADCAST_STICKY,特點是Intent會一直保留到廣播事件結束,而這種廣播也沒有所謂的10秒限制,10秒限制是指普通的廣播如果onReceive方法執行時間太長,超過10秒的時候系統會將這個廣播置為可以幹掉的candidate,一旦系統資源不夠的時候,就會幹掉這個廣播而讓它不執行。

他們的傳送和接收過程類似,這裡只介紹普通廣播。廣播的接收和傳送,本質是一個過程的兩個階段。先說傳送,同樣從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();
        }
    }

直接是AMS的broadcastIntent方法:

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方法,程式碼較多先只看其中一句:

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

這裡表示在Android5.0,預設情況下廣播不會發送給已經停止的應用。其實從Android 3.0開始廣播已經有這種特性,它在Intent中添加了兩個標記位,分別是FLAG_INCLUDE_STOPPED_PACKAGES

表示包含以已經停止的應用,這個時候的廣播會發送給已經停止的應用。

FLAG_EXCLUDE_STOPPED_PACKAGES。

表示不包含已經停止的應用,這個時候的廣播是不會發送給已經停止的應用。

從Android3.1預設新增FLAG_EXCLUDE_STOPPED_PACKAGES,停機狀態無法接到開機廣播。

如果確實需要啟動未啟動的應用,新增標記FLAG_INCLUDE_STOPPED_PACKAGES即可。當兩種標記共存時,以FLAG_INCLUDE_STOPPED_PACKAGES為準。一個應用處於停止狀態有兩種:一是應用處安裝後未執行;二是應用被手動或其他應用強停了。

在broadcastIntentLocked內部會根據intent-filter查詢匹配的廣播接收者並過濾,最終滿足的廣播接受者新增到BroadcastQueue中,接著BroadcastQueue會將廣播發送給對應的廣播接收者。

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

scheduleBroadcastsLocked沒有立即傳送廣播,而是傳送了一個BROCAST_INTENT_MSG型別的訊息。

private final class BroadcastHandler extends Handler {
        public BroadcastHandler(Looper looper) {
            super(looper, null, true);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BROADCAST_INTENT_MSG: {
                    if (DEBUG_BROADCAST) Slog.v(
                            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
                    processNextBroadcast(true);
                } break;
                case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
                case SCHEDULE_TEMP_WHITELIST_MSG: {
                    DeviceIdleController.LocalService dic = mService.mLocalDeviceIdleController;
                    if (dic != null) {
                        dic.addPowerSaveTempWhitelistAppDirect(UserHandle.getAppId(msg.arg1),
                                msg.arg2, true, (String)msg.obj);
                    }
                } break;
            }
        }
    }

Handler處理中呼叫processNextBroadcast(true)方法。

// 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中,系統會遍歷mParallelBroadcasts,並將啟動的廣播發送給他們的接收者。傳送過程有deliverToRegisteredReceiverLocked方法實現。

private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered, int index) {
	……
 performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
……
}

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不可能是null。檢視ApplicationThread的scheduleRegisteredReceiver方法。

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

InnerReceiver的performReceive方法會呼叫LoadedApk.ReceiverDispatcher的performReceive方法。

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

    }


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

            ……
            
            public void run() {
                final BroadcastReceiver receiver = mReceiver;
                final boolean ordered = mOrdered;
                ……               
                final IActivityManager mgr = ActivityManagerNative.getDefault();
               ……
                try {
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    intent.prepareToEnterProcess();
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    receiver.onReceive(mContext, intent);
                } catch (Exception e) {
                    ……..            
         }