Android四大组件系列 Broadcast广播机制(下)

4.4.3 有序广播是如何循环起来的

我们知道,平行广播的循环很简单,只是在一个 while 循环里对每个动态 receiver 执行 deliverToRegisteredReceiverLocked() 即可。

而对有序广播来说,原则上每次 processNextBroadcast() 只会处理一个 BroadcastRecord 的一个 receiver 而已。当然,此时摘下的 receiver 既有可能是动态注册的,也有可能是静态的。

对于动态注册的 receiver,目标进程处理完广播之后,会间接调用 am.finishReceiver() 向 AMS 发出反馈,关于这一步,其实在前面罗列 ReceiverDispatcher 的 performReceive() 时已经出现过了,我们再列一下:

LoadApk.java
#ReceiverDispatcher##InnerReceiver

4.4.3.1 InnerReceiver.performReceive
@Override
public void performReceive(Intent intent, int resultCode, String data,
    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
    
    final LoadedApk.ReceiverDispatcher rd;
    if (intent == null) {
         Log.wtf(TAG, "Null intent received");
         rd = null;
    } else {
         rd = mDispatcher.get();
    }
    ......
    if (rd != null) { // 调用到 ReceiverDispatcher 的 performReceive
        rd.performReceive(intent, resultCode, data, extras,
           ordered, sticky, sendingUser);
    } else {
        ......
    }
}

调用到 ReceiverDispatcher 的 performReceive

LoadApk.java
#ReceiverDispatcher

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 {
            ......
        }
        // 调用 args 的 getRunnable
        if (intent == null || !mActivityThread.post(args.getRunnable())) {
            ......
        }
}

调用 args 的 getRunnable,并 post 到 mActivityThread 的消息队列中执行

LoadApk.java
#ReceiverDispatcher##Args.getRunnable

public final Runnable getRunnable() {
    return () -> {
        final BroadcastReceiver receiver = mReceiver;
        final boolean ordered = mOrdered;
        ......
        final IActivityManager mgr = ActivityManager.getService();
        final Intent intent = mCurIntent;
        ......
        try {
            ClassLoader cl = mReceiver.getClass().getClassLoader();
            intent.setExtrasClassLoader(cl);
            intent.prepareToEnterProcess();
            setExtrasClassLoader(cl);
            // 给 receiver 设定 PendingResult
            receiver.setPendingResult(this);
            // 执行 receiver 的 onReceive 回调
            receiver.onReceive(mContext, intent);
        } catch (Exception e) {
             ......
        }

         // 因为上面已经设置 PendingResult 了,所以走到此分支,调用 finish()
        if (receiver.getPendingResult() != null) {
            finish();
        }
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    };
}

因为 Args 继承自 BroadcastReceiver.PendingResult,所以最终的 finish 调用的是 PendingResult 的函数。

final class Args extends BroadcastReceiver.PendingResult {
    ......
}

BroadcastReceiver.java
#PendingResult.finish

public final void finish() {
     if (mType == TYPE_COMPONENT) {//代表是静态注册的广播
          final IActivityManager mgr = ActivityManager.getService();
          if (QueuedWork.hasPendingWork()) {
               QueuedWork.queue(new Runnable() {
                    @Override public void run() {
                         if (ActivityThread.DEBUG_BROADCAST) Slog.i(
                         ActivityThread.TAG,
                         "Finishing broadcast after work to component "
                          + mToken);
                         sendFinished(mgr);
                     }
               }, false);
          } 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 = ActivityManager.getService();
         sendFinished(mgr);
    }
}

需要说明的是只有有序广播才需要循环处理,这里的有序广播包括:发送给静态注册的 BroadcastReceiver 的广播和发送给动态注册的 BroadcastReceiver 的广播但是声明的是有序;而平行广播是不需要循环处理的。

上面代码分析如下:
静态注册的广播接收者:

  • 当 QueuedWork 工作未完成,即 SharedPreferences 写入磁盘的操作没有完成,则等待完成再执行 sendFinished 方法
  • 当 QueuedWork 工作已完成,则直接调用 sendFinished 方法

动态注册的广播接收者:

  • 当发送的是串行广播,则直接调用 sendFinished 方法

另外常量参数说明:

  • TYPE_COMPONENT:静态注册
  • TYPE_REGISTERED:动态注册
  • TYPE_UNREGISTERED:取消注册

sendFinished

public void sendFinished(IActivityManager am) {
        synchronized (this) {
            ......
            try {
                ......
                // mOrderedHint代表发送是否为串行广播
                if (mOrderedHint) {
                    am.finishReceiver(mToken, mResultCode, 
                    mResultData, mResultExtras,
                    mAbortBroadcast, mFlags);
                } else {
                // 并行广播, 但属于静态注册的广播, 仍然需要告知AMS
                // 注意这里的参数 mToken 指的是 IIntentReceiver
                // AMS 通过这个 token 才能找到对应的
                    // 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) {
            }
        }
}

代码中的 am.finishReceiver() 会通知 AMS,表示用户侧 receiver 已经处理好了,或者至少告一段落了,请 AMS 进行下一步动作。

4.4.3.1 静态注册的receiver的循环

而对于静态注册的 receiver,情况是类似的,最终也是调用 am.finishReceiver() 向 AMS 发出回馈的,只不过发起的动作是在 ActivityThread 的 handleReceiver() 动作中。前文已经列过这个函数了,大家注意下面的句子即可:

private void handleReceiver(ReceiverData data) {
        ......
        String component = data.intent.getComponent().getClassName();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);

        IActivityManager mgr = ActivityManager.getService();

        Application app;
        BroadcastReceiver receiver;
        ContextImpl context;
        try {
            app = packageInfo.makeApplication(false, mInstrumentation);
            context = (ContextImpl) app.getBaseContext();
            ......
            // 通过反射机制生成 receiver
            java.lang.ClassLoader cl = context.getClassLoader();
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.setExtrasClassLoader(cl);
            receiver = packageInfo.getAppFactory()
                    .instantiateReceiver(cl, data.info.name, data.intent);
        } catch (Exception e) {
            ......
        }

        try {
            ......
            sCurrentBroadcastIntent.set(data.intent);
            receiver.setPendingResult(data);
            // 回调 receiver 的 onReceive 函数
            receiver.onReceive(context.getReceiverRestrictedContext(),
                    data.intent);
        } catch (Exception e) {
            ......
        } finally {
            sCurrentBroadcastIntent.set(null);
        }

        if (receiver.getPendingResult() != null) {
        // 调用 BroadcastReceiver.PendingResult 的 finish
        // 这里的 ReceiverData 继承自 BroadcastReceiver.PendingResult
            data.finish();
        }
    }

这个 BroadcastReceiver.PendingResult 的 finish 函数,上面已经详细说明了,在此不再赘述。我们接下来看 AMS 的 finishReceiver 函数

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);
        ......
        final long origId = Binder.clearCallingIdentity();
        try {
            boolean doNext = false;
            BroadcastRecord r;
            BroadcastQueue queue;

            synchronized(this) {
                if (isOnOffloadQueue(flags)) {
                    queue = mOffloadBroadcastQueue;
                } else {
                    queue = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0
                            ? mFgBroadcastQueue : mBgBroadcastQueue;
                }
                // 这里的 who 指的是 IIntentReceiver
                r = queue.getMatchingOrderedReceiver(who);
                if (r != null) {
                    doNext = r.queue.finishReceiverLocked(r, resultCode,
                        resultData, resultExtras, resultAbort, true);
                }
                if (doNext) {
                    r.queue.processNextBroadcastLocked(/*fromMsg=*/ false,
                    /*skipOomAdj=*/ true);
                }
                // updateOomAdjLocked() will be done here
                trimApplicationsLocked(
                OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER);
            }

        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

可以看到最后通过 r.queue.finishReceiverLocked 返回一个是否还有后续广播需要处理,如果有的话,则调用 processNextBroadcastLocked 继续处理下一个广播,如此实现有序广播的循环操作。

4.4.4 有序广播的timeout处理

因为 AMS 很难知道一次广播究竟能不能完全成功递送出去,所以它必须实现一种“时限机制”。

前文在阐述 broadcastIntentLocked() 时,提到过 new 一个 BroadcastRecord 节点,并把这个 BroadcastRecord 节点插入到一个 BroadcastQueue 里的“平行列表”或者“有序列表”中。不过当时我们没有太细说那个 BroadcastQueue,现在我们多加一点儿说明。

实际上系统中有两个 BroadcastQueue,一个叫做“前台广播队列”,另一个叫“后台广播队列”,在 AMS 里是这样定义的:

BroadcastQueue mFgBroadcastQueue;
BroadcastQueue mBgBroadcastQueue;

为什么要搞出两个队列呢?我认为这是因为系统对“广播时限”的要求不同导致的。

对于前台广播队列而言,它里面的每个广播必须在 10 秒之内把广播递送给 receiver,而后台广播队列的时限比较宽,只需 60 秒之内递送到就可以了。具体定义在 AMS 中。

static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;

注意,这个 10 秒或 60 秒限制是针对一个 receiver 而言的。

比如说“前台广播队列”的某个 BroadcastRecord 节点对应了 3 个 receiver,那么在处理这个广播节点时,只要能在 30 秒(3 x 10)之内搞定就可以了。事实上,AMS 系统考虑了更多东西,所以它给一个 BroadcastRecord 的总时限是其所有 receiver 时限之和的 2 倍,在此例中就是 60 秒(2 x 3 x 10)。

对于平行 receiver 而言,不考虑时限,因为动态 receiver 是直接递送到目标进程的,它不考虑目标端是什么时候处理完这个广播的。

然而对于有序 receiver 来说,时限就比较重要了。因为 receiver 之间必须是串行处理的,也就是说上一个 receiver 在没处理完时,系统是不会让下一个 receiver 进行处理的。

从 processNextBroadcast() 的代码来看,在处理有序 receiver 时,BroadcastRecord 里的 nextReceiver 域会记录“下一个应该处理的 receiver” 的标号。

只有在 BroadcastRecord 的所有 receiver 都处理完后,或者 BroadcastRecord 的处理时间超过了总时限的情况下,系统才会把这个 BroadcastRecord 节点从队列里删除。因此我们在 processNextBroadcast() 里看到,通过 BroadcastDispatcher 获取当前 BroadcastRecord 的句子是写死为 mOrderedBroadcasts.remove(0) 的。

在拿到当前 BroadcastRecord 之后,利用 nextReceiver 值拿到当前该处理的 receiver 信息:

int recIdx = r.nextReceiver++;
r.receiverTime = SystemClock.uptimeMillis();
if (recIdx == 0) {
    r.dispatchTime = r.receiverTime;
    r.dispatchClockTime = System.currentTimeMillis();
    ......
}
if (! mPendingBroadcastTimeoutMessage) {
// 这里的 mConstants.TIMEOUT 为 10 秒
    long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
       "Submitting BROADCAST_TIMEOUT_MSG ["
       + mQueueName + "] for " + r + " at " + timeoutTime);
    setBroadcastTimeoutLocked(timeoutTime);
}

final Object nextReceiver = r.receivers.get(recIdx);

 

当然,一开始,nextReceiver 的值只会是 0,表示第一个 receiver 有待处理,此时会给 BroadcastRecord 的 dispatchTime 域赋值。

也就是说,dispatchTime 的意义是标记实际处理 BroadcastRecord 的起始时间,那么这个 BroadcastRecord 所能允许的最大时限值就是:

dispatchTime + 2 * BROADCAST_FG_TIMEOUT / BROADCAST_BG_TIMEOUT * 其 receiver 总数

一旦超过这个时限,而 BroadcastRecord 又没有处理完,那么就强制结束这个 BroadcastRecord 节点:

int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) 
    if ((numReceivers > 0) && (now > r.dispatchTime + 
    (2 * mConstants.TIMEOUT * 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);
        // forcibly finish this broadcast
        broadcastTimeoutLocked(false); 
        forceReceive = true;
        r.state = BroadcastRecord.IDLE;
    }
}

此处调用的 broadcastTimeoutLocked() 的参数是 boolean fromMsg,表示这个函数是否是在处理“时限消息”的地方调用的,因为当前是在 processNextBroadcast() 函数里调用 broadcastTimeoutLocked() 的,所以这个参数为 false。从这个参数也可以看出,另一处判断“处理已经超时”的地方是在消息处理机制里,在那个地方,fromMsg 参数应该设为 true。

大体上说,每当 processNextBroadcast() 准备递送 receiver 时,会调用 setBroadcastTimeoutLocked() 设置一个延迟消息,setBroadcastTimeoutLocked() 的代码如下:

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

 只要我们的 receiver 能及时处理广播,系统就会 cancel 上面的延迟消息。这也就是说,但凡事件的 handleMessage() 开始处理这个消息,就说明 receiver 处理超时了。此时,系统会放弃处理这个receiver,并接着尝试处理下一个 receiver。

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 ["
                      + mQueueName + "]");
                    processNextBroadcast(true);
            } break;
            case BROADCAST_TIMEOUT_MSG: {
                 synchronized (mService) {
                     broadcastTimeoutLocked(true);
                 }
            } break;
        }
    }
}

接着看 broadcastTimeoutLocked

final void broadcastTimeoutLocked(boolean fromMsg) {
    if (fromMsg) {
        mPendingBroadcastTimeoutMessage = false;
    }
    ......
    long now = SystemClock.uptimeMillis();
    BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
    if (fromMsg) {
        ......
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        if (timeoutTime > now) {
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                 "Premature timeout ["
                 + mQueueName + "] @ " + now + 
                 + ": resetting BROADCAST_TIMEOUT_MSG for "
                 + timeoutTime);
                setBroadcastTimeoutLocked(timeoutTime);
                return;
        }
    }

    if (r.state == BroadcastRecord.WAITING_SERVICES) {
        Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
             ? r.curComponent.flattenToShortString() : "(null)"));
        r.curComponent = null;
        r.state = BroadcastRecord.IDLE;
        processNextBroadcast(false);
        return;
    }
    
    final boolean debugging = (r.curApp != null &&
        r.curApp.isDebugging());

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

    ProcessRecord app = null;
    String anrMessage = null;

    Object curReceiver;
    if (r.nextReceiver > 0) {
        curReceiver = r.receivers.get(r.nextReceiver-1);
        r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
    } else {
        curReceiver = r.curReceiver;
    }
    Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
    logBroadcastReceiverDiscardLocked(r);
    if (curReceiver != null && 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;
    }

    if (app != null) {
        anrMessage = "Broadcast of " + r.intent.toString();
    }

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

    // Move on to the next receiver.
    // 结束本次 receiver 处理
   finishReceiverLocked(r, r.resultCode, r.resultData,
          r.resultExtras, r.resultAbort, false);
   // 继续下一个 receiver 的处理
   scheduleBroadcastsLocked();

   if (!debugging && 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));
   }
}
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;
}

可以看到,当一个 receiver 超时后,系统会放弃继续处理它,并再次调用 scheduleBroadcastsLocked(),然后调用 processNextBroadcast 尝试处理下一个 receiver。

五 本地广播LocalBroadcast完全解析

5.1 代码使用

 (1)发送

LocalBroadcastManager lcm=LocalBroadcastManager.getInstance(mContext);
lcm.sendBroadcast(new Intent(ACTION_LOCATION));//发送

 (2)接收

LocalBroadcastManager mLocalBroadcastManager=LocalBroadcastManager.getInstance(this);
mBoradCast = new MyBroadCast(); //定义广播,广播里面接收处理具体事务
IntentFilter intentFilter = new IntentFilter(); //添加监听的广播Action
 intentFilter.addAction(XXX);
//重点在这里,本地注册,本地接收。
mLocalBroadcastManager.registerReceiver(mBoradCast, intentFilter);

  private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) { //处理具体事务
            String action = intent.getAction();
            Log.d(TAG, "onReceive: action = " + action);
            if (action.equals(XXX)) {
            }
        }
    };

Settings 等很多源码应用就使用到了 LocalBroadcastManager 进行消息通讯。

  • 1.在获取LocalBroadcastManager对象实例的时候,这里用了单例模式。并且把外部传进来的Context 转化成了ApplicationContext,有效的避免了当前Context的内存泄漏的问题。这一点我们在设计单例模式框架的时候是值得学习的,看源码可以学习到很多东西。
  • 2.在LocalBroadcastManager构造函数中创建了一个Handler.可见 LocalBroadcastManager 的本质上是通过Handler机制发送和接收消息的。
  • 3.在创建Handler的时候,用了 context.getMainLooper() , 说明这个Handler是在Android 主线程中创建的,广播接收器的接收消息的时候会在Android 主线程,所以我们决不能在广播接收器里面做耗时操作,以免阻塞UI。
  • 4、LocalBroadcastManager采用的是Handler的消息机制来处理的广播,而注册到系统中的是通过Binder机制实现的,速度是应用内广播要快很多。不过由于Handler的消息机制是为了同一个进程的多线程间进行通信的,因而跨进程时无法使用应用内广播。

5.2 源码分析 

1.先来看下LocalBroadcastManager的构造,是使用标准的单例模式实现的。
APP开发者拿到mInstance之后就可以调用registerReceiver、unregisterReceiver、sendBroadcast。

    private final Handler mHandler;
    private static final Object mLock = new Object();
    private static LocalBroadcastManager mInstance;

    public static LocalBroadcastManager getInstance(Context context) {
    synchronized (mLock) {
            if (mInstance == null) {
                mInstance = new LocalBroadcastManager(context.getApplicationContext());
            }
            return mInstance;
        }
    }

    private LocalBroadcastManager(Context context) {
        mAppContext = context;
        mHandler = new Handler(context.getMainLooper()) {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_EXEC_PENDING_BROADCASTS:
                        executePendingBroadcasts();
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        };
    }

看到构造函数中没有做复杂的操作,在主线程初始化了一个Handler.
可以猜测到这个Handler正是用于对广播的分发。

2.广播的注册、反注册、发送流程

如果让我们来自己来实现广播的注册、反注册、发送我们会怎么搞呢?
首先,注册的时候需要提供BroadcastReceiver和对应的IntentFilter,我们可以对这种数据结构进行封装,放到一个类中ReceiverRecord。
然后维护一个ReceiverRecord对象列表,用于记录当前注册了哪些BroadcastReceiver。可以简单使用ArrayList.
在unRegister的时候根据提供的BroadcastReceiver对象,遍历List找出对应的receiver进行移除。
这样每来一个unRegister我们都需要对Receiver列表做一次遍历,开销有点大,在查操作比较多的时候我们可以使用MAP。
HashMap<BroadCastReceiver, ReceiverRecord>
ReceiverRecord中已经包含BroadcastReceiver对象了,所以value直接使用IntentFilte就行了,简化数据结构。
那如果一个Receiver注册了多个IntentFilter呢?比如说一个receiver对象注册两次传入不同的IntentFilter.所以Value需要改造为ArrayList。 最终用于维护当前Reciver对象列表的数据结构是这样事儿的:
HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers.
当删除时可以通过receiver对象为key在map中快速查找并移除。

发送广播的时候呢?我们知道sendBroadcast时只传入了Intent对象,Intent携带了Action用于和已经注册的receiver匹配。在查找receiver时,需要对HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers 的Value进行遍历,每一个Value ArrayList 又需要遍历一次。这个查找的开销实在太大了。
看来我们为了实现Action和receiver的快速匹配需要再维护一个数据结构了。同样是频繁查找的需求使用HashMap.
将Action作为Key,value肯定是与之匹配的receiver了。因为一个Action可能会对应多个receiver,receiver注册的时候可以使用相同的Action.所以value需要使用ArrayList. 当发送广播时可以快速根据Action找到对应的receiver。对了,不仅仅要使用Action匹配,filter中还有其他信息匹配成功之后才能确认是真正的receiver.所以需要使用ReceiverRecord作为value,因为不仅包含了receiver对象,同时包含了IntentFilter.所以最终的数据结构是HashMap<String,ArrayList<ReceiverRecord>>.

先来看两个内部类:

    //内部类ReceiverRecord Receiver记录:用于记录reciver,对应的IntentFilter和是否在broadcast状态
    private static class ReceiverRecord {
        final IntentFilter filter;
        final BroadcastReceiver receiver;
        boolean broadcasting;

        ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
            filter = _filter;
            receiver = _receiver;
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder(128);
            builder.append("Receiver{");
            builder.append(receiver);
            builder.append(" filter=");
            builder.append(filter);
            builder.append("}");
            return builder.toString();
        }
    }

    //BroadcastRecord 广播记录:用于记录广播的intent以及有哪些对应的ReceiverRecord
    private static class BroadcastRecord {
        final Intent intent;
        final ArrayList<ReceiverRecord> receivers;

        BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
            intent = _intent;
            receivers = _receivers;
        }
    }

再来看几个成员变量:

  //维护一个mReceivers Map 记录一个所有的receiver,每一个对应哪些Intentfilters。主要用于记录当前有哪些receiver需要维护接受广播。
     //方便广播的反注册,反注册时可以快速找到filter从而找到Action,从而操作mAction.试想如果没有mReceivers,只能全部遍历mAction找出所有BroadcastRecord,从而找到filter和Action,性能很差。
    private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
            = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
    //维护一个mActions Map 记录所有的Action,每一个对应哪些ReceiverRecord
    private final HashMap<String, ArrayList<ReceiverRecord>> mActions
            = new HashMap<String, ArrayList<ReceiverRecord>>();

    //维护一个List,记录当前正处在等待状态的广播BroadcastRecord,通过BroadcastRecord可以找到intent对应的receivers
    private final ArrayList<BroadcastRecord> mPendingBroadcasts
            = new ArrayList<BroadcastRecord>();

广播的发送过程

    /**
     * Register a receive for any local broadcasts that match the given IntentFilter.
     *
     * @param receiver The BroadcastReceiver to handle the broadcast.
     * @param filter Selects the Intent broadcasts to be received.
     *
     * @see #unregisterReceiver
     */
    public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mReceivers) {
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            //查找receiver有没有在mReceiver记录中,如果不在需要添加进去。
            ArrayList<IntentFilter> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<IntentFilter>(1);
                mReceivers.put(receiver, filters);
            }
            //将IntentFilter加入receiver对应的匹配规则中。filters为mReceivers map的value,类型是IntentFilter的ArrayList。
            //同一个receiver可能有多个IntentFilter。mReceivers就记录了所有的receiver,并且指明每一个receiver所能匹配到的IntentFilter.
            filters.add(filter);
            //开始遍历IntentFilter中的Action.检查Action是否在mActions,如果不在不要添加进去。
            //mActions是Action为Key , ArrayList<ReceiverRecord>为value的MAP。
            //记录了当前有那些Action,并且每个Action对应的Receiver(ReceiverRecord)是哪个。
            for (int i=0; i<filter.countActions(); i++) {
                String action = filter.getAction(i);
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList<ReceiverRecord>(1);
                    mActions.put(action, entries);
                }
                //将ReceiverRcoder对象加入entries:entries是该Action对应的ReceiverRecord列表。
                entries.add(entry);
            }
        }
    }

广播的反注册过程

    /**
     * Unregister a previously registered BroadcastReceiver.  <em>All</em>
     * filters that have been registered for this BroadcastReceiver will be
     * removed.
     *
     * @param receiver The BroadcastReceiver to unregister.
     *
     * @see #registerReceiver
     */
    public void unregisterReceiver(BroadcastReceiver receiver) {
        synchronized (mReceivers) {
            //在Receiver列表中移除要注销的receiver,返回对应的filters.
            ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
            if (filters == null) {
                return;
            }
            //在Action Map中移除对应的receiver
            for (int i=0; i<filters.size(); i++) {
                IntentFilter filter = filters.get(i);
                for (int j=0; j<filter.countActions(); j++) {
                    String action = filter.getAction(j);
                    //根据Action获取receiver列表,移除要删除的receiver.
                    ArrayList<ReceiverRecord> receivers = mActions.get(action);
                    if (receivers != null) {
                        for (int k=0; k<receivers.size(); k++) {
                            if (receivers.get(k).receiver == receiver) {
                                receivers.remove(k);
                                k--;
                            }
                        }
                        //如果发现Action对应的receiver都没有删除掉了,这时候就需要在Action列表中清空。
                        //因为没有receiver来处理这个Action了。
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }

广播的发送过程:

    /**
     * Broadcast the given intent to all interested BroadcastReceivers.  This
     * call is asynchronous; it returns immediately, and you will continue
     * executing while the receivers are run.
     *
     * @param intent The Intent to broadcast; all receivers matching this
     *     Intent will receive the broadcast.
     *
     * @see #registerReceiver
     */
    public boolean sendBroadcast(Intent intent) {
        synchronized (mReceivers) {
            final String action = intent.getAction();
            final String type = intent.resolveTypeIfNeeded(
                    mAppContext.getContentResolver());
            final Uri data = intent.getData();
            final String scheme = intent.getScheme();
            final Set<String> categories = intent.getCategories();

            final boolean debug = DEBUG ||
                    ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
            if (debug) Log.v(
                    TAG, "Resolving type " + type + " scheme " + scheme
                    + " of intent " + intent);

            //根据Action从mActions MAP 中取对应的receivers.
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            if (entries != null) {
                if (debug) Log.v(TAG, "Action list: " + entries);

                ArrayList<ReceiverRecord> receivers = null;
                //遍历receivers找出符合IntentFilter条件的
                for (int i=0; i<entries.size(); i++) {
                    ReceiverRecord receiver = entries.get(i);
                    if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);
                    //如果receiver已经在分发中,不做处理。
                    if (receiver.broadcasting) {
                        if (debug) {
                            Log.v(TAG, "  Filter's target already added");
                        }
                        continue;
                    }

                    //检查receiver是否和发送广播时传入的Intent匹配,并加入符合条件的列表receivers
                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");

                    if (match >= 0) {
                        if (debug) Log.v(TAG, "  Filter matched!  match=0x" +
                                Integer.toHexString(match));
                        if (receivers == null) {
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        receivers.add(receiver);
                        //将receiver 是否在分发中置位true
                        receiver.broadcasting = true;
                    } else {
                        if (debug) {
                            String reason;
                            switch (match) {
                                case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
                                case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
                                case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
                                case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
                                default: reason = "unknown reason"; break;
                            }
                            Log.v(TAG, "  Filter did not match: " + reason);
                        }
                    }
                }
                //将broadcasting置位false 加入mPendingBroadcasts等待队列中.
                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    //发送消息MSG_EXEC_PENDING_BROADCASTS
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
//通过Handler发送消息来处理                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

可以看到最终把要发送的广播加入队列mPendingBroadcasts,然后使用Handler发送消息给主线程处理的,调用executePendingBroadcasts()进行分发。

    private void executePendingBroadcasts() {
        while (true) {
            BroadcastRecord[] brs = null;
            synchronized (mReceivers) {
                final int N = mPendingBroadcasts.size();
                if (N <= 0) {
                    return;
                }
                brs = new BroadcastRecord[N];
                mPendingBroadcasts.toArray(brs);
                mPendingBroadcasts.clear();
            }
            for (int i=0; i<brs.length; i++) {
                BroadcastRecord br = brs[i];
                for (int j=0; j<br.receivers.size(); j++) {
                    br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
                }
            }
        }
    }
}

LocalBroadcast也支持使用同步的方式进行分发:

 /**
     * Like {@link #sendBroadcast(Intent)}, but if there are any receivers for
     * the Intent this function will block and immediately dispatch them before
     * returning.
     */
    public void sendBroadcastSync(Intent intent) {
        if (sendBroadcast(intent)) {
            executePendingBroadcasts();
        }
    }

5.3 总结

  • LocalBroadcast是APP内部维护的一套广播机制,有很高的安全性和高效性。
  • 所以如果有APP内部发送、接收广播的需要应该使用LocalBroadcast。
  • Receiver只允许动态注册,不允许在Manifest中注册。
  • LocalBroadcastManager所发送的广播action,只能与注册到LocalBroadcastManager中BroadcastReceiver产生互动。
  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值