“本地广播”LocalBroadcastManager源码解析

QuibblerQuibbler 2021-01-25 约 21 分钟 1821 次阅读

“本地广播”LocalBroadcastManager源码解析

在BroadcastReceiver:广播的使用一文中总结了关于广播的基础知识,关于本地广播和普通广播的区别总结如下:

①BroadcastReceiver一般用于应用间消息;LocalBroadcastManager应用内消息传递。

②BroadcastReceiver是Binder进程间通信;LocalBroadcastManager内部用Handler实现,效率更高。

③BroadcastReceiver需要考虑安全等问题;LocalBroadcastManager应用内部使用,安全性高。

仔细看看本地广播LocalBroadcastManager源码是如何实现的,源码并不多,一共320行。巧妙的使用Handler实现,利用IntentFilter匹配广播,以及和Context中BroadcastReceiver相关方法一样的接口设计。

1、内部成员

先看看LocalBroadcastManager内部有哪些成员:一个单例、一个Handle以及若干保存BroadcastReceiver的集合映射。

1.1、LocalBroadcastManager单例

在应用中通过getInstance(Context )获取LocalBroadcastManager实例:

LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);

源码中这样的单例模式很常见,效率比较高:

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

1.2、内部Handler

本地广播接收的关键之一是这个Handler,很多优秀的功能设计都是通过Handler,足以见得Handler在Android开发中的重要地位!

static final int MSG_EXEC_PENDING_BROADCASTS = 1;
    private final Handler mHandler;

获取单例的时候通过私有构造方法初始化mHandler,通过主线程的Looper初始化,广播消息在主线程回调执行。收到MSG_EXEC_PENDING_BROADCASTS消息时执行executePendingBroadcasts()方法(后面详细看看)。

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);
                }
            }
        };
    }

1.3、全局集合

LocalBroadcastManager内部有两映射,用来保存注册的广播接收器对应的记录、Action对应的接收器集合。后面注册、反注册以及广播分发的时候会用到。

private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers = new HashMap<>();
    private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
    
    //待处理的广播接收器,在sendBroadcast中添加值,在executePendingBroadcasts()中清空
    private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();

BroadcastReceiver和ReceiverRecord是两个静态内部类保存对应关系:

private static final class ReceiverRecord {
        final IntentFilter filter;
        final BroadcastReceiver receiver;
        boolean broadcasting;
        boolean dead;
        ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
            filter = _filter;
            receiver = _receiver;
        }
    }

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

2、注册和反注册本地广播

看看本地广播注册和反注册是如何实现的,这两个方法和Context中注册普通广播的签名类型一样。

2.1、注册

注册本地广播接收器的函数registerReceiver(BroadcastReceiver receiver, IntentFilter filter)很面熟吧,和Context中相关方法的签名一样。

public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mReceivers) {
            //将BroadcastReceiver和IntentFilter包装成ReceiverRecord一条记录。
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            //从全局mReceivers中获取receiver对应的filters过滤器
            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
            if (filters == null) {
                //如果新注册的广播没有,那么就新建一个,添加到receiver和过滤器的映射中
                filters = new ArrayList<>(1);
                mReceivers.put(receiver, filters);
            }
            //将ReceiverRecord添加到过滤器中
            filters.add(entry);
            
            //遍历IntentFilter过滤器
            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);
                }
                entries.add(entry);
            }
        }
    }

2.2、反注册

从本地广播集合mReceivers、mActions中移除注册的广播接收器:

public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
        synchronized (mReceivers) {
            final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
            if (filters == null) {
                return;
            }
            for (int i=filters.size()-1; i>=0; i--) {
                final ReceiverRecord filter = filters.get(i);
                filter.dead = true;
                for (int j=0; j<filter.filter.countActions(); j++) {
                    final String action = filter.filter.getAction(j);
                    final ArrayList<ReceiverRecord> receivers = mActions.get(action);
                    if (receivers != null) {
                        for (int k=receivers.size()-1; k>=0; k--) {
                            final ReceiverRecord rec = receivers.get(k);
                            if (rec.receiver == receiver) {
                                rec.dead = true;
                                receivers.remove(k);
                            }
                        }
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }

3、发送本地广播和分发广播

LocalBroadcastManager是如何通过Handler发送本地广播的?

3.1、发送本地广播

开发者通过LocalBroadcastManager的sendBroadcast(Intent intent)方法发送本地广播,函数前面和Context发送广播的一致,虽然实现完全不一样。当注册的广播接收器中有能够处理该条广播的接收器时,向Handler发送一条消息MSG_EXEC_PENDING_BROADCASTS。

/**
     * 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.
     */
    public boolean sendBroadcast(@NonNull Intent intent) {
        synchronized (mReceivers) {
                ...
                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    //添加能够处理这条广播的接收器当待处理广播集合mPendingBroadcasts中
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

还有另外一个方法sendBroadcastSync(Intent intent),发送同步广播,发送后立即执行

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

3.2、分发本地广播

每次发送一个广播,都会挨个遍历注册的广播接收器,看看有没有能够处理该广播的广播接收器。如果有就回调广播接收器重写的onReceive()方法。

@SuppressWarnings("WeakerAccess") /* synthetic access */
    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清空
                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);
                    }
                }
            }
        }
    }

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w