美文网首页
2020-04-17-Android本地广播的实现原理

2020-04-17-Android本地广播的实现原理

作者: 耿望 | 来源:发表于2020-04-18 16:38 被阅读0次

    getInstance

    LocalBroadcastManager实现了一个单例模式,每个进程只能获取到一个实例。

        @NonNull
        public static LocalBroadcastManager getInstance(@NonNull 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);
                    }
                }
            };
        }
    

    构造函数传入了Application Context,并且初始化了一个运行在主线程的Handler对象。

    registerReceiver

    首先看下LocalBroadcastManager内部有两个HashMap结构分别保存了receiver和action的键值对。

        private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
                = new HashMap<>();
        private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
    

    注册广播接收器的方法跟全局广播的形式是一样的,传入一个receiver跟一个intentFilter。
    注释1 初始化一个ReceiverRecord对象,它是一个内部静态类,保存了对应的receiver和intentFilter;
    注释2 将ReceiverRecord对象加入到ArrayList列表中,因为同一个receiver可能被注册多次,有多个intentFilter;
    注释3 将每个action对应的ReceiverRecord对象加入到列表中,同样的,同一个action可能被多个receiver监听。

        public void registerReceiver(@NonNull BroadcastReceiver receiver,
                @NonNull IntentFilter filter) {
            synchronized (mReceivers) {
                ReceiverRecord entry = new ReceiverRecord(filter, receiver);//1
                ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
                if (filters == null) {
                    filters = new ArrayList<>(1);
                    mReceivers.put(receiver, filters);
                }
                filters.add(entry);//2
                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);//3
                    }
                    entries.add(entry);
                }
            }
        }
    

    sendBroadcast

    发送广播的时候需要去找到对应监听的receiver,对广播进行分发。因为本地广播是进程内共享的,可能出现竞争关系,这里使用了synchronized进行同步操作。
    注释1 从mActions中找到这个广播的action对应的列表;
    注释2 遍历列表找到对应的receiver;
    注释3 根据IntentFilter的匹配规则进行匹配;
    注释4 将匹配成功的receiver加入到列表,并且将broadcasting标志置为true;
    注释5 处理完成后将broadcasting标志复位;
    注释6 将所有匹配到的receiver封装成BroadcastRecord对象加入到mPendingBroadcasts列表中;
    注释7 通过handler消息对广播进行分发处理。

        public boolean sendBroadcast(@NonNull 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);
    
                ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());//1
                if (entries != null) {
                    if (debug) Log.v(TAG, "Action list: " + entries);
    
                    ArrayList<ReceiverRecord> receivers = null;
                    for (int i=0; i<entries.size(); i++) {
                        ReceiverRecord receiver = entries.get(i);//2
                        if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);
    
                        if (receiver.broadcasting) {
                            if (debug) {
                                Log.v(TAG, "  Filter's target already added");
                            }
                            continue;
                        }
    
                        int match = receiver.filter.match(action, type, scheme, data,
                                categories, "LocalBroadcastManager");//3
                        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.broadcasting = true;//4
                        } 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);
                            }
                        }
                    }
    
                    if (receivers != null) {
                        for (int i=0; i<receivers.size(); i++) {
                            receivers.get(i).broadcasting = false;//5
                        }
                        mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));//6
                        if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                            mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);//7
                        }
                        return true;
                    }
                }
            }
            return false;
        }
    

    executePendingBroadcasts

    最后看下广播的分发过程,注释1处调用了接收器的onReceive方法。

        void executePendingBroadcasts() {
            while (true) {
                final BroadcastRecord[] brs;
                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++) {
                    final BroadcastRecord br = brs[i];
                    final int nbr = br.receivers.size();
                    for (int j=0; j<nbr; j++) {
                        final ReceiverRecord rec = br.receivers.get(j);
                        if (!rec.dead) {
                            rec.receiver.onReceive(mAppContext, br.intent);//1
                        }
                    }
                }
            }
        }
    

    sendBroadcastSync

    本地广播还有一个比较特殊的地方,是可以使用同步方法,保证广播发送的时序。
    实际上普通发送广播的方式,真正分发的操作是通过handler实现的一个异步过程。
    而sendBroadcastSync方法是直接调用executePendingBroadcasts方法,可能会阻塞等待操作实行完成。

        public void sendBroadcastSync(@NonNull Intent intent) {
            if (sendBroadcast(intent)) {
                executePendingBroadcasts();
            }
        }
    

    参考

    Android本地广播和全局广播的区别及实现原理

    相关文章

      网友评论

          本文标题:2020-04-17-Android本地广播的实现原理

          本文链接:https://www.haomeiwen.com/subject/lewsvhtx.html