LocalBroadcastManager本地广播原理解析

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

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

image

基本使用

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

implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'

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

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



    

    

    


image

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

来看看代码的实现部分:

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

来看下运行效果:

image

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:



    

    


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

image

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

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> mReceivers
            = new HashMap<>();

    //维护不同Action的ReceiverRecord集合的HashMap
    private final HashMap> 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 filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<>(1);
                mReceivers.put(receiver, filters);
            }
            filters.add(entry);
            //mActions同样也是这个道理
            for (int i=0; i entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList(1);
                    mActions.put(action, entries);
                }
                entries.add(entry);
            }
        }
    }

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

    public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
        synchronized (mReceivers) {
            final ArrayList 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 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 mPendingBroadcasts = new ArrayList<>();
    
    private static final class BroadcastRecord {
        final Intent intent;
        final ArrayList receivers;

        BroadcastRecord(Intent _intent, ArrayList _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 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 entries = mActions.get(intent.getAction());
            if (entries != null) {
                if (debug) Log.v(TAG, "Action list: " + entries);

                //初始化一个要发送的记录
                ArrayList receivers = null;
                for (int i=0; i= 0) {
                        if (debug) Log.v(TAG, "  Filter matched!  match=0x" +
                                Integer.toHexString(match));
                        if (receivers == null) {
                            receivers = new ArrayList();
                        }
                        //往要发送的集合里添加
                        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

最后看看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

将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来代替实现相关功能。这里就简单举例一下:

image

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

创建一个需要用到的ViewModel:

class LiveDataViewModel : ViewModel() {

    /**
     * 只暴露不可变的LiveData给外部。
     */
    private val _value = MutableLiveData()
    val value: LiveData 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
        })
    }
}

最终的效果如下:

image

END

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

你可能感兴趣的:(LocalBroadcastManager本地广播原理解析)