Androidソース解析四大コンポーネントシリーズ(八)---放送のいくつかの問題の深い理解


上の文章に続いて、この文章は主に前の知識を総括して、しかもいくつかの細部の問題を理解して、放送のメカニズムに対する理解を深めて、例えば放送の秩序があるのはどのように秩序を保証しますか?放送遮断メカニズムはどのように実現されますか?放送送信がタイムアウトした場合、どのように処理されますか?registerReceiverメソッドの戻り値は何に使いますか?粘性放送など.
Androidソース解析の4つのコンポーネントシリーズ(5)—ブロードキャストの登録プロセス
Androidソース解析四大コンポーネントシリーズ(六)—放送の処理過程
Androidソース解析の4つのコンポーネントシリーズ(7)—放送の送信プロセス
1、放送関連データ構造の再理解
  • ReceiverDispatcher:クライアントブロードキャスト配信者オブジェクト,第1編では,ReceiverDispatcherの内部クラスInnerReceiverがbinderオブジェクトであり,AMSとの伝達と通信に用いられることが明らかになった.
  • ReceiverList:ArrayListから継承され、Receiverのbinderオブジェクトおよび登録されたBroadcastFilterリストが格納されます.AMSではfinal HashMap
  • が定義されている
    2、秩序放送はどのように秩序を保証するのか
    前の記事ではprocessNextBroadcast()は1つのBroadcastRecordの1つの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が実行されたためです.下にある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の値により2つの分岐がある.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であるため、2番目のブランチを歩きます.
       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が実行する.次は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);
            }

    データを見てみましょう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は2つのブロードキャストキューBroadcastQueue、mFgBroadcastQueue、フロントキューのタイムアウト時間は10秒、mBgBroadcastQueue、バックグラウンドキューのタイムアウト時間は60秒で、ブロードキャストが所定の時間内に処理されなければANRが発生し、あなたのブロードキャストがフロントブロードキャストキューに入りたい場合は、送信時にintentにIntentを追加します.FLAG_RECEIVER_FOREGROUNDタグは、追加しない場合、システムのデフォルトはバックグラウンドブロードキャストです.mFgBroadcastQueueはより高い権限を持ち、優先的に処理されます.
    プロセスNextBroadcastメソッドには次のコードがあり,ブロードキャストタイムアウトに関係し,タイムアウトすると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を処理する開始時間+ブロードキャストのデフォルトのタイムアウト時間*ブロードキャスト受信者の数です.ところで、この公式はなぜこのように設計されているのでしょうか.1つのフロントのブロードキャストメッセージに受信者が2人いる場合、20秒(2 x 10)以内に完了すればよいし、1つ目のメッセージが15秒、2つ目のメッセージが4.99秒、1つ目のメッセージが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、放送ブロック処理分析
    ブロードキャストメッセージは、複数の受信者を有することができ、順序付きブロードキャストに対して次から次へと処理され、優先度の高い受信者は優先的に実行することができ、ブロードキャストの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が次のブロードキャストを処理することを通知し、第1節で分析したが、最終的に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                    。
     ```  
        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();
    }

    放送の4つの文章を書き終わって、1波のサービスを分析するつもりです