LocalBroadcastManager本地广播原理解析

4,054 阅读9分钟

之前有被问到过Android普通广播和本地广播的区别,所以打算分析下本地广播的实现原理以及简单结束两者的区别,算是对自我的一此源码学习总结。

目前官网文档上是说LocalBroadcastManager被废弃了,如果想使用的话需要自行依赖或者使用LiveData(后面会简单介绍下其用法)来代替它。

说明

基本使用

首先我们在项目添加本地广播的依赖

implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'

界面是Activity包裹两个Fragment,一个负责接受广播刷新界面,另一个负责发送广播。

布局比较简单,贴个大概就晓得了。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".activity.localbroad.LocalBroadActivity">

    <fragment
        android:id="@+id/fragmentOne"
        android:name="com.vico.livedatademo.fragment.localbroad.LocalBroadOneFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        tools:layout="@layout/fragment_local_broad_one" />

    <View
        android:layout_width="match_parent"
        android:layout_height="3dp"
        android:background="@android:color/black" />

    <fragment
        android:id="@+id/fragmentTwo"
        android:name="com.vico.livedatademo.fragment.localbroad.LocalBroadTwoFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        tools:layout="@layout/fragment_local_broad_two" />

</LinearLayout>

布局

从上图可以看到,除本地广播之外我还增加了普通广播发送的功能,目的也是为了下文的跨应用接收做验证。

来看看代码的实现部分:

class LocalBroadOneFragment : Fragment() {

    companion object {
        const val LOCAL = "com.vico.livedatademo_local"
        const val EXPORT = "com.vico.livedatademo_export"
    }

    private lateinit var localBroadcast: LocalBroadcast
    private lateinit var exportBroadcast: ExportBroadcast

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_local_broad_one, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        //注册本地广播
        localBroadcast = LocalBroadcast()
        //指定action
        val localIntentFilter = IntentFilter(LOCAL)
        LocalBroadcastManager.getInstance(requireContext()).registerReceiver(localBroadcast, localIntentFilter)

        //注册普通广播
        exportBroadcast = ExportBroadcast()
        val exportIntentFilter = IntentFilter(EXPORT)
        requireContext().registerReceiver(exportBroadcast, exportIntentFilter)
    }

    override fun onDetach() {
        //别忘了最后要进行注销的操作
        LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(localBroadcast)
        requireContext().unregisterReceiver(exportBroadcast)
        super.onDetach()
    }

    //自定义广播,用来接收本地广播发来的数据
    inner class LocalBroadcast : BroadcastReceiver() {

        override fun onReceive(context: Context?, intent: Intent?) {
            context ?: return
            intent ?: return
            if (intent.action == LOCAL) {
                tv1.text = intent.getStringExtra(LocalBroadTwoFragment.LOCAL_EVENT) ?: ""
            }
        }
    }

    inner class ExportBroadcast : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            context ?: return
            intent ?: return
            if (intent.action == EXPORT) {
                tv2.text = intent.getStringExtra(LocalBroadTwoFragment.EXPORT_EVENT) ?: ""
            }
        }

    }
}
class LocalBroadTwoFragment : Fragment() {

    companion object {
        const val LOCAL_EVENT = "local_event"
        const val EXPORT_EVENT = "export_event"
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_local_broad_two, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        //发送一条包含时间戳内容的本地广播,指定action
        btnLocal.setOnClickListener {
            val localIntent = Intent(LocalBroadOneFragment.LOCAL)
            localIntent.putExtra(LOCAL_EVENT, System.currentTimeMillis().toString())
            LocalBroadcastManager.getInstance(requireContext()).sendBroadcast(localIntent)
        }

        //发送一条包含时间戳内容的普通广播,指定action
        btnNormal.setOnClickListener {
            val exportIntent = Intent(LocalBroadOneFragment.EXPORT)
            exportIntent.putExtra(EXPORT_EVENT, (System.currentTimeMillis() / 100).toString())
            requireContext().sendBroadcast(exportIntent)
        }
    }
}

来看下运行效果:

发送广播

em........这样好像也没什么区别嘛,没事我们在新启一个应用来看看能否接收当前应用发出的广播。

新建的Phone Module代码跟上面接收广播的类似

OtherActivity:

class OtherActivity : AppCompatActivity() {

    companion object {
        const val LOCAL = "com.vico.livedatademo_local"
        const val EXPORT = "com.vico.livedatademo_export"
    }

    private lateinit var localBroadcast: LocalBroadcast
    private lateinit var exportBroadcast: ExportBroadcast

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_other)
        localBroadcast = LocalBroadcast()
        val localIntentFilter = IntentFilter(LOCAL)
        LocalBroadcastManager.getInstance(this).registerReceiver(localBroadcast, localIntentFilter)

        exportBroadcast = ExportBroadcast()
        val exportIntentFilter = IntentFilter(EXPORT)
        registerReceiver(exportBroadcast, exportIntentFilter)
    }

    override fun onDestroy() {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(localBroadcast)
        unregisterReceiver(exportBroadcast)
        super.onDestroy()
    }

    inner class LocalBroadcast : BroadcastReceiver() {

        override fun onReceive(context: Context?, intent: Intent?) {
            context ?: return
            intent ?: return
            if (intent.action == LOCAL) {
                tv1.text = intent.getStringExtra("local_event") ?: ""
            }
        }
    }

    inner class ExportBroadcast : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            context ?: return
            intent ?: return
            if (intent.action == EXPORT) {
                tv2.text = intent.getStringExtra("export_event") ?: ""
            }
        }

    }
}

activity_other.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    tools:context=".OtherActivity">

    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="来自本地广播的值" />

    <TextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="来自普通广播的值" />

</LinearLayout>

运行一下,看下这个新建的应用能否接收到另一个应用发送出来的广播。

发送广播2

确实本地广播无法做到跨应用发送,那么其内部的实现原理是什么呢?下面我们一起来看看

LocalBroadcastManager原理

通过对LocalBroadcastManager的使用我们可以得知,LocalBroadcastManger采用了单例设计模式,将其构造函数私有化。看下构造函数里做了些上什么:

    private final Context mAppContext;
    
    static final int MSG_EXEC_PENDING_BROADCASTS = 1;

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

    @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;
        //初始化一个在主线程运行的Handler
        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,在接收到消息后就会执行待处理的广播。

好的,接下去看看注册registerReceiver函数里做了什么:

    //维护不同BroadcastReceiver的ReceiverRecord集合的HashMap
    private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
            = new HashMap<>();

    //维护不同Action的ReceiverRecord集合的HashMap
    private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
    
    private static final class ReceiverRecord {
        final IntentFilter filter;
        final BroadcastReceiver receiver;

        boolean broadcasting;
        //是否被注销
        boolean dead;

        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);
            if (dead) {
                builder.append(" DEAD");
            }
            builder.append("}");
            return builder.toString();
        }
    }
    
    
    /**
     * 注册一个接收匹配给定的IntentFilter任何本地广播
     *
     * @param receiver 处理广播的BroadCastReceiver
     * @param filter 
     */
    public void registerReceiver(@NonNull BroadcastReceiver receiver,
            @NonNull IntentFilter filter) {
        synchronized (mReceivers) {
            //将filter和receiver包装成ReceiverRecord对象
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            //查找mReceivers这个HashMap里是否有对应的key,有则在value里添加一条记录,无则put
            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<>(1);
                mReceivers.put(receiver, filters);
            }
            filters.add(entry);
            //mActions同样也是这个道理
            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);
            }
        }
    }

可以看出registerReceiver函数是对两个HashMap的新增操作,那么unregisterReceiver同理是对两个HashMap的删除操作:

    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);
                //将ReceiverRecord标记为已注销
                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) {
                                //将ReceiverRecord标记为已注销
                                rec.dead = true;
                                receivers.remove(k);
                            }
                        }
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }

好的,看完注册和注销,我们看看发送广播sendBroadcast(Intent intent)是怎么实现的:

    private static final String TAG = "LocalBroadcastManager";
    private static final boolean DEBUG = false;
    
    private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
    
    private static final class BroadcastRecord {
        final Intent intent;
        final ArrayList<ReceiverRecord> receivers;

        BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
            intent = _intent;
            receivers = _receivers;
        }
    }
    
    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);
            
            //查询是否有action匹配的记录,无则结束
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            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);
                    if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);

                    //跳过正在执行的记录
                    if (receiver.broadcasting) {
                        if (debug) {
                            Log.v(TAG, "  Filter's target already added");
                        }
                        continue;
                    }

                    //看IntentFilter是否匹配,不匹配就结束
                    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.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);
                        }
                    }
                }

                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    //往待执行广播的集合填充
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    //Handler发送一条消息,最终在handleMessage里会执行处理广播的操作executePendingBroadcasts
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

最后看看executePendingBroadcasts

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

将mPendingBroadcasts里元素进行复制并清空,复制后的数组遍历执行receiver。本地广播的流程到这里就结束了,下面讲讲它和普通的广播有什么区别。

和普通广播的区别在哪?

两个不同的应用在Android系统中相当于两个不同的进程。当前应用发出的普通广播能够被其他应用所接收到,那么这势必是一个进程间通信的过程(IPC)。

广播的注册分为静态和动态两种注册方式,其中静态注册的广播在应用安装时由系统自动完成注册,具体点是PMS(PackageMangerService)完成的。这里就只简单分析下广播的动态注册过程。动态注册的过程从ContextWrapperregisterReceiver方法开始。ContextWrapper并没有做什么实际工作,将过程交给了ContextImpl来完成。

    @Override
    public Intent registerReceiver(
        BroadcastReceiver receiver, IntentFilter filter) {
        return mBase.registerReceiver(receiver, filter);
    }

ContextImplregisterReceiver方法调用了自己的registerReceiverInterval方法,实现如下:

    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            final Intent intent = ActivityManager.getService().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                    broadcastPermission, userId, flags);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

系统首先从mPackageInfo获取IIntentReceiver对象,然后再采用跨进程的方式向AMS发送广播注册的请求。IIntentReceiver是一个Binder接口,它的具体实现是LoadedApk.ReceiverDispatcher.InnerRecevier,ReceiverDispatcher的内部同时保存了BroadcastReceiverInnerReceiver,这样当接收到广播时,ReceiverDispatcher可以很方便地调用BroadcastReceiveronReceive方法。

相比而言,使用LocalBroadcastManager来收发本地广播效率更高(无需进行进程间通信),并且无需考虑其他应用在收发广播时带来的任何安全问题。

使用LiveData替换?

开头说到LocalBroadcastManager已经被废弃,需要引入依赖支持。可以使用LiveData来代替实现相关功能。这里就简单举例一下:

LiveData

界面如上图所示,Fragment1、Fragment2以及包裹它们的Activity都共同持有一个ViewModel。点击Fragment2中的按钮,Fragment1中的TextView和中间的分割线会发生变化。

创建一个需要用到的ViewModel:

class LiveDataViewModel : ViewModel() {

    /**
     * 只暴露不可变的LiveData给外部。
     */
    private val _value = MutableLiveData<String>()
    val value: LiveData<String> get() = _value

    fun setValue() {
        _value.value = System.currentTimeMillis().toString()
    }
}

在Fragment2中更改value的值:

class LiveDataTwoFragment : Fragment() {

    private lateinit var viewModel: LiveDataViewModel

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_live_data_two, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProvider(requireActivity()).get(LiveDataViewModel::class.java)

        btnLiveData.setOnClickListener {
            viewModel.setValue()
        }
    }
}

在Fragment1和Activity中观察value的变化刷新UI:

class LiveDataActivity : AppCompatActivity() {

    private lateinit var viewModel: LiveDataViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)

        viewModel = ViewModelProvider(this).get(LiveDataViewModel::class.java)

        viewModel.value.observe(this, Observer {
            val random = Random()
            val r = random.nextInt(255)
            val g = random.nextInt(255)
            val b = random.nextInt(255)
            viewLine.setBackgroundColor(Color.rgb(r, g, b))
        })
    }
}
class LiveDataOneFragment : Fragment() {

    private lateinit var viewModel: LiveDataViewModel

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_live_data_one, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProvider(requireActivity()).get(LiveDataViewModel::class.java)

        viewModel.value.observe(requireActivity(), Observer { value ->
            tvLiveData.text = value
        })
    }
}

最终的效果如下:

LiveData.gif

END

文章到这里就已经结束了,这一次就LocalBroadcastManager的实现原理和与普通广播的区别做了介绍,以及相关的其他方案。文中的LiveData+ViewModel的实现只是其中一种方式而已,还有其他诸多优秀的方案可以选择,比如:EventBus,RxBus,LiveEventBus等等。 由于个人水平的原因,可能无法更深层次的去介绍,希望能多多包涵。我也同广大的开发者一样,一步步地在慢慢成长。