本地广播用途

Android本地广播 android本地广播原理_ide

本地广播用于同一个app内通信(是否可以多进程通信?),且其他app无法接收到本地广播的消息,可以大大提高安全性,且只能动态注册。

本地广播的使用和原理分析

本地广播使用到的几个类

Android本地广播 android本地广播原理_android 本地广播原理_02

1、public final class LocalBroadcastManager这个类是无法继承的,直接使用即可,后续我们深入分析这个类。

Android本地广播 android本地广播原理_Android本地广播_03

2、public abstract class BroadcastReceiver这个类是抽象类,在使用过程中一般需要继承,实现onReceive函数。后续我们会分析为什么需要继承这个类且为什么要实现onReceive函数。

Android本地广播 android本地广播原理_android_04

3、public class IntentFilter implements Parcelable这个类是用于添加本地广播Inten过滤动作的,且我们后续会深入分析这个类。

本地广播实现源代码

Android本地广播 android本地广播原理_本地广播_05

1、新建一个BroadcastTest工程,创建一个空的活动即可,在MainActivity中增加若干私有成员,新建一个LocalReceiver类继承BroadcastReceiver并重写onReceive函数,以下是一些代码片段,后续我也会先贴出片段代码方便分析,最后再贴出完整代码。

public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private static final String TAG="MainActivity";
private LocalReceiver localReceiver;
private LocalBroadcastManager localBroadcastManager;
private static final String localBroadcastAction = "com.example.broadcasttest.LOCAL_BROADCAST";
private class LocalReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received a localbroadcast", Toast.LENGTH_SHORT).show();
}
}

Android本地广播 android本地广播原理_android_06

我们跳进BroadcastReceiver找到onReceive函数,源代码如下:

*
* @param context The Context in which the receiver is running.
* @param intent The Intent being received.
*/
public abstract void onReceive(Context context, Intent intent);

Android本地广播 android本地广播原理_ide_07

从这里我们可以看出onReceive函数是抽象函数,所以我们需要重新实现这个函数。刚开始学习Android的时候通常我们会看到书上说我们要实现xxx函数,但是通常不会告诉我们为什么要重新实现这个函数,而且函数的参数是哪些也都不知道。这个时候我们可以点开源码,阅读源码知根知底。关于如何调用onReceive后面会详细分析。

Android本地广播 android本地广播原理_Android本地广播_08

2、创建一个按钮控件,方便后面测试,按下按钮使用本地广播发送一条消息,并用Toast显示。

Android本地广播 android本地广播原理_android 本地广播原理_09

3、在MainActivity的onCreate函数中进行初始化变量,注册按钮监听器。

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
Log.d(TAG, "button clicked");
Intent intent = new Intent(localBroadcastAction);
localBroadcastManager.sendBroadcast(intent);
//localBroadcastManager.sendBroadcastSync(intent);
}
});
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(localBroadcastAction);
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver, intentFilter);

Android本地广播 android本地广播原理_本地广播_10

进入LocalBroadcastManager源码我们发现该类的构造函数是私有的,也就是我们无法通过new创建LocalBroadcastManager的实例,而且还发现它有一个静态私有的LocalBroadcastManager成员变量,那么意思很明显了,只能通过一个公有的方法获取LocalBroadcastManager单例对象实例。

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

Android本地广播 android本地广播原理_android_11

通过代码我们知道,基于懒汉式单例模式通过公有方法getInstance创建对象实例,所以我们只能通过调用getInstance函数获取LocalBroadcastManager对象实例。从构造函数我们也能大概猜出,初始化的过程中创建了一个线程处理发送的广播消息。由于本地广播可以发送多条消息,因此要想接收到指定的本地广播消息,则需要一个广播消息ID。IntentFilter则是通过存储本地广播消息ID,并和广播接收器BroadcastReceiver绑定在一起实现接收指定的广播数据。

* @param action Name of the action to match, such as Intent.ACTION_VIEW.
*/
public final void addAction(String action) {
if (!mActions.contains(action)) {
mActions.add(action.intern());
}
}
@UnsupportedAppUsage
private final ArrayList mActions;
* @see #unregisterReceiver
*/
public void registerReceiver(@NonNull BroadcastReceiver receiver,
@NonNull IntentFilter filter) {
synchronized (mReceivers) {
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
ArrayList filters = mReceivers.get(receiver);
if (filters == null) {
filters = new ArrayList<>(1);
mReceivers.put(receiver, filters);
}
filters.add(entry);
for (int i=0; i
String action = filter.getAction(i);
ArrayList entries = mActions.get(action);
if (entries == null) {
entries = new ArrayList(1);
mActions.put(action, entries);
}
entries.add(entry);
}
}
}
private static final class ReceiverRecord {
final IntentFilter filter;
final BroadcastReceiver receiver;
boolean broadcasting;
boolean dead;
ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
filter = _filter;
receiver = _receiver;
}

IntentFilter将action存储在ArrayList中,LocalBroadcastManager的registerReceiver函数将传入的receiver和filter通过ReceiverRecord绑定在一起,实现了接收特定本地广播消息的功能。

Android本地广播 android本地广播原理_ide_12

4、调用sendBroadcast将本地广播消息发送出去(实际上只是把数据放在了一个消息队列里面,线程异步将消息发送出去),我们直接点开sendBroadcast源码,以下是代码片段。

if (receivers != null) {
for (int i=0; i
receivers.get(i).broadcasting = false;
}
mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
}
return true;
}

Android本地广播 android本地广播原理_ide_13

从源码我们知道,存储Intent并发送一条广播消息到消息队列里面,并且将receiver和BroadcastRecord联系在一起,且将BroadcastRecord存储在mPendingBroadcasts里面。我们进入LocalBroadcastManager的构造函数调用的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
final BroadcastRecord br = brs[i];
final int nbr = br.receivers.size();
for (int j=0; j
final ReceiverRecord rec = br.receivers.get(j);
if (!rec.dead) {
rec.receiver.onReceive(mAppContext, br.intent);
}
}
}
}

Android本地广播 android本地广播原理_Android本地广播_14

从mPendingBroadcasts中取出BroadcastRecord对象实例,获取调用receiver,调用onReceive函数。这就是我们为什么可以在onReceive函数中接收到本地广播消息了。

总结

Android本地广播 android本地广播原理_android 本地广播原理_15

本地广播的基本原理和使用我们已经讲得差不多了,其实还有很多没有讲到。但是没办法,要是把全部实现源代码追踪下去,无穷无尽。所以点到为止,继续学习后续的知识,先应付工作先。

Android本地广播 android本地广播原理_android_16

我们在学习Android的时候不仅仅学习表面如何使用,也要适当的追踪源码,学习底层实现原理。Android实在是知识太丰富了,有很多可以学习的地方,如果不是时间有限,真想把所有知识点追踪下去。

完整代码

package com.example.broadcasttest;
import androidx.appcompat.app.AppCompatActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.app.admin.NetworkEvent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private static final String TAG="MainActivity";
private LocalReceiver localReceiver;
private LocalBroadcastManager localBroadcastManager;
private static final String localBroadcastAction = "com.example.broadcasttest.LOCAL_BROADCAST";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
Log.d(TAG, "button clicked");
Intent intent = new Intent(localBroadcastAction);
localBroadcastManager.sendBroadcast(intent);
//localBroadcastManager.sendBroadcastSync(intent);
}
});
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(localBroadcastAction);
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver, intentFilter);
}
private class LocalReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "received a localbroadcast", Toast.LENGTH_SHORT).show();
}
}
public void onDestroy(){
super.onDestroy();
localBroadcastManager.unregisterReceiver(localReceiver);
}
}