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

接上篇文章辈赋,這篇文章主要是總結前面知識鲫忍,并且了解一些細節(jié)問題,加深對廣播機制的理解钥屈,比如有播有序是怎么保證有序的悟民?廣播攔截機制是怎么實現的?廣播發(fā)送超時了是怎么處理的篷就?registerReceiver方法發(fā)返回值有什么用射亏?粘性廣播等等。

Android源碼解析四大組件系列(五)---廣播的注冊過程

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

Android源碼解析四大組件系列(七)---廣播的發(fā)送過程

1、廣播相關數據結構的再次理解

  • ReceiverDispatcher: 客戶端廣播分發(fā)者對象智润,第一篇講的很清楚了及舍,ReceiverDispatcher的內部類InnerReceiver為binder對象,用于與AMS的傳遞與通信窟绷。

  • ReceiverList: 繼承自ArrayList锯玛,存放了Receiver的binder對象以及其注冊的BroadcastFilter列表。AMS中定義了
    final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();key為InnerReceiver的binder對象钾麸,值為ReceiverList更振,ReceiverList內部記錄的是動態(tài)注冊的廣播接收者,mRegisteredReceivers只有動態(tài)注冊的時候才會有內容饭尝。

  • BroadcastFilter: 封裝了IntentFilter肯腕,描述動態(tài)廣播,是動態(tài)廣播節(jié)點钥平。

  • ResolveInfo:Parcelable子類实撒,描述靜態(tài)廣播,是靜態(tài)廣播節(jié)點涉瘾。

  • IntentResolver: 解析Intent践美,在addFilter時即進行解析璃岳。其內部有mSchemeToFilter,mActionToFilter,mTypedActionToFilter三個map對象庶艾。key為對應的action(scheme或者type)骂束,value為Filter传于。

  • BroadcastRecord:描述一個廣播碉克, 將intent等一堆信息,封裝成BroadcastRecord赁还,交給BroadcastQueue進行處理妖泄。

  • BroadcastQueue: BroadcastQueue為Broadcast處理隊列,分為前臺隊列mFgBroadcastQueue和后臺隊列mBgBroadcastQueue艘策,mFgBroadcastQueue會有更高的權限蹈胡,被優(yōu)先處理。mFgBroadcastQueue和mBgBroadcastQueue兩個隊列中都含有mOrderedBroadcasts和mParallelBroadcasts兩個列表用來表示有序廣播列表和無序廣播列表朋蔫。

2罚渐、有序廣播是怎么保證有序的

上一篇文章中說了processNextBroadcast()只會處理一個BroadcastRecord的一個receiver,那怎么將廣播傳遞給下一個receiver呢驯妄?廣播接受者有“動態(tài)”和“靜態(tài)”之分搅轿,廣播消息也有“串行”和“并行”之分,或者叫“有序”和“無序”之分富玷。廣播的處理方式跟廣播的接收者和廣播消息類型有關系。有序廣播是怎么保證有序的這個問題,得分情況討論赎懦,對于動態(tài)注冊的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之前励两,執(zhí)行了 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是動態(tài)注冊廣播接收者傳進來的盲憎,值是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) {
             //再次執(zhí)行processNextBroadcast處理廣播
               r.queue.processNextBroadcast(false);
           }
           trimApplications();
       } finally {
           Binder.restoreCallingIdentity(origId);
       }
   }

上面是分析了動態(tài)的廣播接收者是怎么按照一個接著一個處理的溺森。在看看靜態(tài)注冊的receiver,回到靜態(tài)廣播回調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方法之前屏积, 執(zhí)行了 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秒,如果廣播沒有在規(guī)定的時間內處理完就會發(fā)生ANR,如果你想你的廣播進入前臺廣播隊列题画,那么在發(fā)送的時候默辨,在intent中加入Intent.FLAG_RECEIVER_FOREGROUND標記,如果不加苍息,系統(tǒng)默認是后臺廣播缩幸。mFgBroadcastQueue會有更高的權限壹置,被優(yōu)先處理。

在processNextBroadcast方法中有下面一段代碼表谊,與廣播超時有關系钞护,一旦超時就會出現ANR。

do {
       int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
       if (mService.mProcessesReady && r.dispatchTime > 0) {
           long now = SystemClock.uptimeMillis();
           //廣播消息的第一個ANR監(jiān)測機制
           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)之內搞定就可以了立润,也可能第一個消息執(zhí)行了15秒,第二個消息執(zhí)行4.99秒媳板,即使第一消息超過了10秒的規(guī)定桑腮,也不會出現ANR。但是系統(tǒng)任務繁忙蛉幸,可能有其他活要干破讨,我們要盡可能的減少ANR的發(fā)生,所以前面乘以2倍奕纫。

假設現在廣播超時還沒處理提陶,滿足if條件,就會進入匹层,打印Hung broadcast ["+ mQueueName + "] discarded after timeout failure....的log隙笆,然后執(zhí)行 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;
       }
   }

如果廣播消息能夠處理完畢升筏,就會執(zhí)行cancelBroadcastTimeoutLocked撑柔,將超時的Message移除掉。

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

如果廣播消息沒有在timeout時間內處理掉您访,下面BroadcastHandler發(fā)送的消息就會執(zhí)行铅忿。

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) {
       //正在執(zhí)行dexopt,返回
           if (mService.mDidDexOpt) {
               // Delay timeouts until dexopt finishes.
               mService.mDidDexOpt = false;
               long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
               setBroadcastTimeoutLocked(timeoutTime);
               return;
           }
       //系統(tǒng)還沒有進入ready狀態(tài)
           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;
           }
           //如果當前正在執(zhí)行的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;
           }
       }

       //當前正在執(zhí)行的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超時后肢扯,系統(tǒng)會放棄繼續(xù)處理它給出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() {
           //內部創(chuàng)建ANR顯示的Dialog
           mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation);
       }
   }

4、廣播攔截處理分析

廣播消息可以有多個接收者肛循,對于有序廣播是一個接著一個處理的铭腕,優(yōu)先級高的接收者可以優(yōu)先執(zhí)行,并且可以調用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處理下一個廣播,在第一小節(jié)已經分析過搭伤,最終進入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傳進來是true只怎,
                  doNext = r.queue.finishReceiverLocked(r, resultCode,
                      resultData, resultExtras, resultAbort, true);
              }
          }
   //調用processNextBroadcast處理廣播
          if (doNext) {
              r.queue.processNextBroadcast(false);
          }
          trimApplications();
      } finally {
          Binder.restoreCallingIdentity(origId);
      }
  }

processNextBroadcast方法中有一個檢查廣播有沒有發(fā)送完畢的邏輯。

  do {
     .....
     r = mOrderedBroadcasts.get(0);
     //檢查廣播有沒有發(fā)送完怜俐,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()函數來發(fā)送,用此函數發(fā)送的廣播會一直滯留季稳,當有匹配此廣播的廣播接收器被注冊后擅这,該廣播接收器就會收到此條信息。使用此函數需要發(fā)送廣播時景鼠,需要獲得BROADCAST_STICKY權限仲翎。粘性廣播可以使用廣播接收器進行接收,但是正確的接收方式是調用registerReceiver能接受廣播莲蜘,信息將在調用registerReceiver的返回值中給出谭确。對于粘性廣播的發(fā)送,和普通廣播的發(fā)送方式是一致的票渠,例子來自與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) {
    //收到廣播
  }
}
<!--使用粘性廣播發(fā)送權限-->
<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吧

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市问顷,隨后出現的幾起案子昂秃,更是在濱河造成了極大的恐慌禀梳,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肠骆,死亡現場離奇詭異算途,居然都是意外死亡,警方通過查閱死者的電腦和手機蚀腿,發(fā)現死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進店門嘴瓤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人莉钙,你說我怎么就攤上這事廓脆。” “怎么了磁玉?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵停忿,是天一觀的道長。 經常有香客問我蚊伞,道長席赂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任时迫,我火速辦了婚禮颅停,結果婚禮上,老公的妹妹穿的比我還像新娘别垮。我一直安慰自己便监,他們只是感情好,可當我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布碳想。 她就那樣靜靜地躺著烧董,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胧奔。 梳的紋絲不亂的頭發(fā)上逊移,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天,我揣著相機與錄音龙填,去河邊找鬼胳泉。 笑死,一個胖子當著我的面吹牛岩遗,可吹牛的內容都是我干的扇商。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼宿礁,長吁一口氣:“原來是場噩夢啊……” “哼案铺!你這毒婦竟也來了?” 一聲冷哼從身側響起梆靖,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤控汉,失蹤者是張志新(化名)和其女友劉穎笔诵,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體姑子,經...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡乎婿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了街佑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谢翎。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖舆乔,靈堂內的尸體忽然破棺而出岳服,到底是詐尸還是另有隱情,我是刑警寧澤希俩,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站纲辽,受9級特大地震影響颜武,放射性物質發(fā)生泄漏。R本人自食惡果不足惜拖吼,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一鳞上、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吊档,春花似錦篙议、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至香璃,卻和暖如春这难,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背葡秒。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工姻乓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人眯牧。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓蹋岩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親学少。 傳聞我的和親對象是個殘疾皇子剪个,可洞房花燭夜當晚...
    茶點故事閱讀 43,566評論 2 349

推薦閱讀更多精彩內容