Android原始碼解析四大元件系列(八)---廣播幾個問題的深入理解

Looperjing發表於2017-07-20

接上篇文章,這篇文章主要是總結前面知識,並且瞭解一些細節問題,加深對廣播機制的理解,比如有播有序是怎麼保證有序的?廣播攔截機制是怎麼實現的?廣播傳送超時了是怎麼處理的?registerReceiver方法發返回值有什麼用?粘性廣播等等。

Android原始碼解析四大元件系列(五)---廣播的註冊過程

Android原始碼解析四大元件系列(六)---廣播的處理過程

Android原始碼解析四大元件系列(七)---廣播的傳送過程

###1、廣播相關資料結構的再次理解

  • ReceiverDispatcher: 客戶端廣播分發者物件,第一篇講的很清楚了,ReceiverDispatcher的內部類InnerReceiver為binder物件,用於與AMS的傳遞與通訊。

  • ReceiverList: 繼承自ArrayList,存放了Receiver的binder物件以及其註冊的BroadcastFilter列表。AMS中定義了
    final HashMap mRegisteredReceivers = new HashMap<>();key為InnerReceiver的binder物件,值為ReceiverList,ReceiverList內部記錄的是動態註冊的廣播接收者,mRegisteredReceivers只有動態註冊的時候才會有內容。

  • BroadcastFilter: 封裝了IntentFilter,描述動態廣播,是動態廣播節點。

  • ResolveInfo:Parcelable子類,描述靜態廣播,是靜態廣播節點。

  • IntentResolver: 解析Intent,在addFilter時即進行解析。其內部有mSchemeToFilter,mActionToFilter,mTypedActionToFilter三個map物件。key為對應的action(scheme或者type),value為Filter。

  • BroadcastRecord:描述一個廣播, 將intent等一堆資訊,封裝成BroadcastRecord,交給BroadcastQueue進行處理。

  • BroadcastQueue: BroadcastQueue為Broadcast處理佇列,分為前臺佇列mFgBroadcastQueue和後臺佇列mBgBroadcastQueue,mFgBroadcastQueue會有更高的許可權,被優先處理。mFgBroadcastQueue和mBgBroadcastQueue兩個佇列中都含有mOrderedBroadcasts和mParallelBroadcasts兩個列表用來表示有序廣播列表和無序廣播列表。

###2、有序廣播是怎麼保證有序的

上一篇文章中說了processNextBroadcast()只會處理一個BroadcastRecord的一個receiver,那怎麼將廣播傳遞給下一個receiver呢?廣播接受者有“動態”和“靜態”之分,廣播訊息也有“序列”和“並行”之分,或者叫“有序”和“無序”之分。廣播的處理方式跟廣播的接收者和廣播訊息型別有關係。有序廣播是怎麼保證有序的這個問題,得分情況討論,對於動態註冊的receiver,先回到最終onReceive回撥的地方,分析如下:

 static final class ReceiverDispatcher {

     .....

        final class Args extends BroadcastReceiver.PendingResult implements Runnable {
            .....
             public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                    boolean ordered, boolean sticky, int sendingUser) {
                //mRegistered傳進來的是true
                super(resultCode, resultData, resultExtras,
                        mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
                        sticky, mIIntentReceiver.asBinder(), sendingUser, intent.getFlags());
                mCurIntent = intent;
                mOrdered = ordered;
            }
            public void run() {
                 .....
                try {
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    intent.prepareToEnterProcess();
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    //廣播的onReceive方法回撥
                    receiver.onReceive(mContext, intent);
                } catch (Exception e) {
                    if (mRegistered && ordered) {
                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                "Finishing failed broadcast to " + mReceiver);
                        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) {
                    finish();
                }
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
        }
    }複製程式碼

因為在呼叫onReceive之前,執行了 receiver.setPendingResult(this),所以在下面receiver.getPendingResult()就不是null,則就進入BroadcastReceiver的內部類PendingResult的finish方法。

 public final void finish() {
            if (mType == TYPE_COMPONENT) {
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                if (QueuedWork.hasPendingWork()) {
                  ......
                    QueuedWork.singleThreadExecutor().execute( new Runnable() {
                        @Override public void run() {
                            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                    "Finishing broadcast after work to component " + mToken);
                            sendFinished(mgr);
                        }
                    });
                } else {
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing broadcast to component " + mToken);
                    sendFinished(mgr);
                }
            } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                        "Finishing broadcast to " + mToken);
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                sendFinished(mgr);
            }
        }複製程式碼

finish方法中根據mType的值有兩個分支。mType是PendingResult的成員變數,在PendingResult的建構函式中進行賦值的。

     public PendingResult(int resultCode, String resultData, Bundle resultExtras, int type,
                boolean ordered, boolean sticky, IBinder token, int userId, int flags) {
            mResultCode = resultCode;
            mResultData = resultData;
            mResultExtras = resultExtras;
            mType = type;
            mOrderedHint = ordered;
            mInitialStickyHint = sticky;
            mToken = token;
            mSendingUser = userId;
            mFlags = flags;
        }複製程式碼

這個構造方法是在BroadcastReceiver.PendingResult的子類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;
            }
}複製程式碼

由於mRegistered是動態註冊廣播接收者傳進來的,值是true,所以上面mType的值是TYPE_REGISTERED,由於是有序廣播ordered值是true,那麼mOrderedHint為true,所以要走第二個分支:

   if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                        "Finishing broadcast to " + mToken);
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                sendFinished(mgr);複製程式碼

BroadcastReceiver的sendFinished方法如下:

 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) {
                        am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
                                mAbortBroadcast, mFlags);
                    } else {
                        // This broadcast was sent to a component; it is not ordered,
                        // but we still need to tell the activity manager we are done.
                        am.finishReceiver(mToken, 0, null, null, false, mFlags);
                    }
                } catch (RemoteException ex) {
                }
            }
        }複製程式碼

有序廣播mOrderedHint值為true,所以進入到AMS的finishReceiver方法。

public void finishReceiver(IBinder who, int resultCode, String resultData,
            Bundle resultExtras, boolean resultAbort, int flags) {
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + who);

        // 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) {
              //再次執行processNextBroadcast處理廣播
                r.queue.processNextBroadcast(false);
            }
            trimApplications();
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }複製程式碼

上面是分析了動態的廣播接收者是怎麼按照一個接著一個處理的。在看看靜態註冊的receiver,回到靜態廣播回撥onReceive方法的地方。

private void handleReceiver(ReceiverData data) {
     ....
        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) {
         ....
        }

        try {
            Application app = packageInfo.makeApplication(false, mInstrumentation);
             ....
            ContextImpl context = (ContextImpl)app.getBaseContext();
            sCurrentBroadcastIntent.set(data.intent);
            receiver.setPendingResult(data);
            //回撥廣播的onReceive方法
            receiver.onReceive(context.getReceiverRestrictedContext(),data.intent);

        } catch (Exception e) {
            ....
            }
        } finally {
            sCurrentBroadcastIntent.set(null);
        }

        if (receiver.getPendingResult() != null) {
            data.finish();
        }
    }複製程式碼

在回撥onReceiver方法之前, 執行了 receiver.setPendingResult(data),所以下面receiver.getPendingResult() != null成立,走 data.finish(),data是ReceiverData物件,handleReceiver方法傳進來的,在scheduleReceiver方法中初始化。

  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);
        }複製程式碼

我們看 data.finish()方法

  public final void finish() {
            if (mType == TYPE_COMPONENT) {
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                if (QueuedWork.hasPendingWork()) {
                    QueuedWork.singleThreadExecutor().execute( new Runnable() {
                        @Override public void run() {
                            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                    "Finishing broadcast after work to component " + mToken);
                            sendFinished(mgr);
                        }
                    });
                } else {
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing broadcast to component " + mToken);
                    sendFinished(mgr);
                }
            } else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                        "Finishing broadcast to " + mToken);
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                sendFinished(mgr);
            }
        }複製程式碼

此時mType分析後值是TYPE_COMPONENT,同樣會走sendFinished,後面AMS的處理邏輯是一樣的,不贅述。

###3、廣播超時是怎麼處理的?

AMS維護了兩個廣播佇列BroadcastQueue,mFgBroadcastQueue,前臺佇列的超時時間是10秒,mBgBroadcastQueue,後臺佇列的超時時間是60秒,如果廣播沒有在規定的時間內處理完就會發生ANR,如果你想你的廣播進入前臺廣播佇列,那麼在傳送的時候,在intent中加入Intent.FLAG_RECEIVER_FOREGROUND標記,如果不加,系統預設是後臺廣播。mFgBroadcastQueue會有更高的許可權,被優先處理。

在processNextBroadcast方法中有下面一段程式碼,與廣播超時有關係,一旦超時就會出現ANR。

do {
        int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
        if (mService.mProcessesReady && r.dispatchTime > 0) {
            long now = SystemClock.uptimeMillis();
            //廣播訊息的第一個ANR監測機制
            if ((numReceivers > 0) &&
                    (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                Slog.w(TAG, "Hung broadcast ["
                        + mQueueName + "] discarded after timeout failure:"
                        + " now=" + now
                        + " dispatchTime=" + r.dispatchTime
                        + " startTime=" + r.receiverTime
                        + " intent=" + r.intent
                        + " numReceivers=" + numReceivers
                        + " nextReceiver=" + r.nextReceiver
                        + " state=" + r.state);
                broadcastTimeoutLocked(false); // 超時處理
                forceReceive = true;
                r.state = BroadcastRecord.IDLE;
            }
        }
         //判斷廣播有沒有處理完畢
        if (r.receivers == null || r.nextReceiver >= numReceivers
                || r.resultAbort || forceReceive) {
            // No more receivers for this broadcast!  Send the final
            // result if requested...
            if (r.resultTo != null) {
                try {     
                    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) {
                   ......
                }
            }

    } while (r == null);複製程式碼

廣播的超時機制是針對有序廣播來說的,無序廣播一次性全部處理了,肯定不會超時,超時的這段邏輯都在broadcastTimeoutLocked中,首先判斷是否超時,公式:r.dispatchTime + 2×mTimeoutPeriod×numReceivers,現在解釋一下這幾個時間:

  • dispatchTime的意義是標記實際處理BroadcastRecord的起始時間,有序廣播是一個接著一個進行處理的,第一次dispatchTime=0,並不會進入該條件判斷

  • mTimeoutPeriod由當前BroadcastQueue的型別決定(mFgBroadcastQueue為10秒,mBgBroadcastQueue為60秒)

   // How long we allow a receiver to run before giving up on it.
 static final int BROADCAST_FG_TIMEOUT = 10*1000;
  static final int BROADCAST_BG_TIMEOUT = 60*1000;

  mFgBroadcastQueue = new BroadcastQueue(this, mHandler,  "foreground", BROADCAST_FG_TIMEOUT, false);
  mBgBroadcastQueue = new BroadcastQueue(this, mHandler,  "background", BROADCAST_BG_TIMEOUT, true);複製程式碼

所以上面公式翻譯過來就是:實際處理BroadcastRecord的起始時間+廣播預設的超時時間*廣播接收者的數量。話說回來,這個公式為什麼要這麼設計呢?如果一個前臺的廣播訊息有兩個接收者,那麼在20秒(2 x 10)之內搞定就可以了,也可能第一個訊息執行了15秒,第二個訊息執行4.99秒,即使第一訊息超過了10秒的規定,也不會出現ANR。但是系統任務繁忙,可能有其他活要幹,我們要儘可能的減少ANR的發生,所以前面乘以2倍。

假設現在廣播超時還沒處理,滿足if條件,就會進入,列印Hung broadcast ["+ mQueueName + "] discarded after timeout failure....的log,然後執行 broadcastTimeoutLocked(false)強制停止廣播,broadcastTimeoutLocked相關程式碼程式碼如下:

    final void broadcastTimeoutLocked(boolean fromMsg) {
            .....
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            if (timeoutTime > now) {
                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                        "Premature timeout ["
                        + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
                        + timeoutTime);
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
        }

      .....
    }複製程式碼

內部呼叫setBroadcastTimeoutLocked()設定一個延遲訊息

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

如果廣播訊息能夠處理完畢,就會執行cancelBroadcastTimeoutLocked,將超時的Message移除掉。

final void cancelBroadcastTimeoutLocked() {
    if (mPendingBroadcastTimeoutMessage) {
        mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
        mPendingBroadcastTimeoutMessage = false;
    }複製程式碼

如果廣播訊息沒有在timeout時間內處理掉,下面BroadcastHandler傳送的訊息就會執行。

private final class BroadcastHandler extends Handler {
      .....
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
              .....
                case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
              .....
            }
        }
    }複製程式碼

再次進入broadcastTimeoutLocked方法裡面

 final void broadcastTimeoutLocked(boolean fromMsg) {
        //傳進來是ture 
       if (fromMsg) {
            mPendingBroadcastTimeoutMessage = false;
        }
        //佇列沒有廣播處理了,返回
        if (mOrderedBroadcasts.size() == 0) {
            return;
        }

        long now = SystemClock.uptimeMillis();
        BroadcastRecord r = mOrderedBroadcasts.get(0);
        if (fromMsg) {
        //正在執行dexopt,返回
            if (mService.mDidDexOpt) {
                // Delay timeouts until dexopt finishes.
                mService.mDidDexOpt = false;
                long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
        //系統還沒有進入ready狀態
            if (!mService.mProcessesReady) {
                // Only process broadcast timeouts if the system is ready. That way
                // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
                // to do heavy lifting for system up.
                return;
            }
            //如果當前正在執行的receiver沒有超時,則重新設定廣播超時
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            if (timeoutTime > now) {
                // We can observe premature timeouts because we do not cancel and reset the
                // broadcast timeout message after each receiver finishes.  Instead, we set up
                // an initial timeout then kick it down the road a little further as needed
                // when it expires.
                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                        "Premature timeout ["
                        + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
                        + timeoutTime);
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
        }

        //當前正在執行的receiver沒有超時,則重新設定廣播超時,處理下一條廣播
        BroadcastRecord br = mOrderedBroadcasts.get(0);
        if (br.state == BroadcastRecord.WAITING_SERVICES) {
            // In this case the broadcast had already finished, but we had decided to wait
            // for started services to finish as well before going on.  So if we have actually
            // waited long enough time timeout the broadcast, let's give up on the whole thing
            // and just move on to the next.
            Slog.i(TAG, "Waited long enough for: " + (br.curComponent != null
                    ? br.curComponent.flattenToShortString() : "(null)"));
            br.curComponent = null;
            br.state = BroadcastRecord.IDLE;
            processNextBroadcast(false);
            return;
        }

        Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r. receiver
                + ", started " + (now - r.receiverTime) + "ms ago");
        r.receiverTime = now;
        r.anrCount++;

        // Current receiver has passed its expiration date.
        if (r.nextReceiver <= 0) {
            Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0");
            return;
        }

        ProcessRecord app = null;
        String anrMessage = null;

        Object curReceiver = r.receivers.get(r.nextReceiver-1);
        r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
        Slog.w(TAG, "Receiver during timeout: " + curReceiver);
        logBroadcastReceiverDiscardLocked(r);
        if (curReceiver instanceof BroadcastFilter) {
            BroadcastFilter bf = (BroadcastFilter)curReceiver;
            if (bf.receiverList.pid != 0
                    && bf.receiverList.pid != ActivityManagerService.MY_PID) {
                synchronized (mService.mPidsSelfLocked) {
                    app = mService.mPidsSelfLocked.get(
                            bf.receiverList.pid);
                }
            }
        } else {
            app = r.curApp;
        }

    //程式存在,anrMessage賦值
        if (app != null) {
            anrMessage = "Broadcast of " + r.intent.toString();
        }

        if (mPendingBroadcast == r) {
            mPendingBroadcast = null;
        }

        // Move on to the next receiver.
        finishReceiverLocked(r, r.resultCode, r.resultData,
                r.resultExtras, r.resultAbort, false);
        //處理下一條廣播
        scheduleBroadcastsLocked();

        if (anrMessage != null) {
            // Post the ANR to the handler since we do not want to process ANRs while
            // potentially holding our lock.
            mHandler.post(new AppNotResponding(app, anrMessage));
        }
    }複製程式碼

所以當一個receiver超時後,系統會放棄繼續處理它給出ANR提示,並再次呼叫scheduleBroadcastsLocked(),嘗試處理下一個receiver,

private final class AppNotResponding implements Runnable {
        private final ProcessRecord mApp;
        private final String mAnnotation;

        public AppNotResponding(ProcessRecord app, String annotation) {
            mApp = app;
            mAnnotation = annotation;
        }

        @Override
        public void run() {
            //內部建立ANR顯示的Dialog
            mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation);
        }
    }複製程式碼

###4、廣播攔截處理分析
廣播訊息可以有多個接收者,對於有序廣播是一個接著一個處理的,優先順序高的接收者可以優先執行,並且可以呼叫BroadcastReceiver的abortBroadcast()方法攔截廣播,如果我們在receiver的onReceive()中呼叫這個方法,那麼它後面的接收者就不會收到廣播。

public abstract class BroadcastReceiver {
    private PendingResult mPendingResult;

  public final void abortBroadcast() {
         checkSynchronousHint();
         mPendingResult.mAbortBroadcast = true;
     }
 }複製程式碼

把BroadcastReceiver::PendingResult的成員變數mAbortBroadcast設定成true,

 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() {
              .....
                try {
                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    intent.prepareToEnterProcess();
                    setExtrasClassLoader(cl);
              //設定PendingResult,這個PendingResult中mAbortBroadcast為true
                    receiver.setPendingResult(this);
                    receiver.onReceive(mContext, intent);
                } catch (Exception e) {
                    .....
                }

                if (receiver.getPendingResult() != null) {
                    //告知AMS處理下一個廣播
                    finish();
                }

            }
        }複製程式碼

finish()會告知AMS處理下一個廣播,在第一小節已經分析過,最終進入AMS的finishReceiver方法

  public void finishReceiver(IBinder who, int resultCode, String resultData,
            Bundle resultExtras, boolean resultAbort, int flags) {
       .....
        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) {
          //resultAbort傳進來是truedoNext = r.queue.finishReceiverLocked(r, resultCode,
                        resultData, resultExtras, resultAbort, true);
                }
            }
   //呼叫processNextBroadcast處理廣播
            if (doNext) {
                r.queue.processNextBroadcast(false);
            }
            trimApplications();
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }複製程式碼

processNextBroadcast方法中有一個檢查廣播有沒有傳送完畢的邏輯。

    do {
       .....
     r = mOrderedBroadcasts.get(0);
       //檢查廣播有沒有傳送完,resultAbort為=ture
        if (r.receivers == null || r.nextReceiver >= numReceivers
                || r.resultAbort || forceReceive) {
              .....
            //mOrderedBroadcasts裡刪除廣播訊息
            mOrderedBroadcasts.remove(0);
            r = null;
            looped = true;
            continue;
        }
    } while (r == null);複製程式碼

當resultAbort為=ture時候,廣播訊息從mOrderedBroadcasts刪除,後面也就收不到廣播了。

###5、理解粘性廣播

sticky廣播通過Context.sendStickyBroadcast()函式來傳送,用此函式傳送的廣播會一直滯留,當有匹配此廣播的廣播接收器被註冊後,該廣播接收器就會收到此條資訊。使用此函式需要傳送廣播時,需要獲得BROADCAST_STICKY許可權。粘性廣播可以使用廣播接收器進行接收,但是正確的接收方式是呼叫registerReceiver能接受廣播,資訊將在呼叫registerReceiver的返回值中給出。對於粘性廣播的傳送,和普通廣播的傳送方式是一致的,例子來自與Android 粘性廣播StickyBroadcast的使用

private void sendStickyBroadcast(){
    Intent i = new Intent(); 
    i.setAction(StickyBroadcastReceiver.Action); 
    i.putExtra("info", "sticky broadcast has been receiver"); 
    sendStickyBroadcast(i);
    Log.i("Other","sticky broadcast send ok!"); 
}複製程式碼

可以使用BroadcastReceiver來接收

public class StickyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
    //收到廣播
    }
}複製程式碼
<!--使用粘性廣播傳送許可權-->
<uses-permission android:name="android.permission.BROADCAST_STICKY" />複製程式碼
IntentFilter intentFilter = new IntentFilter(StickyBroadcastReceiver.Action);
 Intent data = registerReceiver(null, intentFilter);
 if(data!=null&&StickyBroadcastReceiver.Action.equals(data.getAction()))  {
   Toast.makeText(this, data.getStringExtra("info"), Toast.LENGTH_SHORT).show();
}複製程式碼

好了廣播的四篇文章寫完了,準備在分析一波Service吧

相關文章