LocalBroadcastManager“真香”预警!!!

前言

我们在Android应尽量避免使用隐式Intent广播传递信息,为什么这么说?原因有下面几点:

  1. 意外接收:如果同时维护几个项目,不同项目中难免会存在代码复用的情况,这时若安装了两个注册过同样 Action 广播的APP,一个APP通过Context.sendBroadcast()发送的隐式广播也会被另一个APP接收到,并进行相应的操作,可能会产生意想不到的风险。
  2. 敏感信息外泄:发送的隐式广播,可能会被恶意应用注册监听该广播的 receiver 获取到Intent中传递的敏感信息,并进行其他危险的操作。
  3. Intent拦截:如果发送的广播为使用Context.sendOrderedBroadcast()方法发送的有序广播,优先级较高的恶意 receiver 若直接丢弃该广播,会导致服务无法正常使用,或者广播结果被填充恶意数据。

基于以上的几点,会发现使用隐式Intent广播风险很高,那么怎么解决这个问题呢?首先,我们需要明确广播是否仅限于应用内使用。若需要在应用间传递广播,应尽量避免传递敏感信息;否则,可以使用LocalBroadcastManager.sendBroadcast()实现,这样就避免了意外接收广播,敏感信息外泄和Intent拦截的风险。

 

注意:这篇文章是从我个人博客复制过来的,代码格式显示有问题,懒得整理了。可以点击LocalBroadcastManager这里阅读。

 

LocalBroadcastManager源码分析

LocalBroadcastManager的源码并不多,总共也就不到300行,我们来分析下是怎么它的实现。

单例模式

首先,从下面这几行代码会发现LocalBroadcastManager使用了延迟加载的单例模式保证类对象的唯一性。

1
2
3
4
5
6
7
8
9
@NonNull
public static LocalBroadcastManager getInstance(@NonNull Context context) {
    synchronized(mLock) {
        if (mInstance == null) {
            mInstance = new LocalBroadcastManager(context.getApplicationContext());
        }
        return mInstance;
    }
}

内部类

再看LocalBroadcastManager的两个静态内部类。

1
2
3
4
5
6
7
8
9
private static final class ReceiverRecord {
    final IntentFilter filter;
    final BroadcastReceiver receiver;
    // 请自行分析这两个变量的作用
    boolean broadcasting;
    boolean dead;
    // 省略了构造函数
    // 省略了toString函数
}
1
2
3
4
5
private static final class BroadcastRecord {
    final Intent intent;
    final ArrayList<LocalBroadcastManager.ReceiverRecord> receivers;
    // 省略了构造函数
}

ReceiverRecord类记录了Intent过滤器和BroadcastReceiver对象,BroadcastRecord记录了发送的Intent和一个ReceiverRecord的集合,这个集合是用来做什么的呢?目前猜测存放的应该是需要接收Intent的所有BroadcastReceiver对象,带着这个疑问继续看它的关键成员变量。

成员变量和关键函数

1
2
3
private final HashMap<BroadcastReceiver, ArrayList<LocalBroadcastManager.ReceiverRecord>> mReceivers = new HashMap();
private final HashMap<String, ArrayList<LocalBroadcastManager.ReceiverRecord>> mActions = new HashMap();
private final ArrayList<LocalBroadcastManager.BroadcastRecord> mPendingBroadcasts = new ArrayList();

想理解这几个变量的作用,需要去看对应的代码。

下面的代码是LocalBroadcastManager#registerReceiver()函数,
从代码注释中的 标识1 标识2 可以看出变量名为mReceivers的HashMap存放通过注册的BroadcastReceiver(广播接收者)对象和存放了广播接收者信息的ReceiverRecord。
从 标识4 标识5 不难看出变量名为mActions的HashMap存放的是通过LocalBroadcastManager#registerReceiver()方法注册的Action事件和这个事件所对应的所有ReceiverRecord。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 注册BroadcastReceiver(广播接收者)
public void registerReceiver(@NonNull BroadcastReceiver receiver, @NonNull IntentFilter filter) {
    synchronized(this.mReceivers) {
        LocalBroadcastManager.ReceiverRecord entry = new LocalBroadcastManager.ReceiverRecord(filter, receiver);
        ArrayList<LocalBroadcastManager.ReceiverRecord> filters = (ArrayList)this.mReceivers.get(receiver);
        if (filters == null) {
            filters = new ArrayList(1);
            /** 标识1:以BroadcastReceiver为键,ReceiverRecord集合为值存放到mReceivers中 */
            this.mReceivers.put(receiver, filters);
        }
        /** 标识2:将ReceiverRecord存放到ReceiverRecord集合中 */
        filters.add(entry);

        /** 标识3:遍历需要注册的Action集合 */
        for(int i = 0; i < filter.countActions(); ++i) {
            String action = filter.getAction(i);
            ArrayList<LocalBroadcastManager.ReceiverRecord> entries = (ArrayList)this.mActions.get(action);
            if (entries == null) {
                entries = new ArrayList(1);
                /** 标识4:以Action为键,Action对应的ReceiverRecord集合为值,存放到mActions中 */
                this.mActions.put(action, entries);
            }

            /** 标识5:将ReceiverRecord存放到Action对应的ReceiverRecord集合中 */
            entries.add(entry);
        }

    }
}

怎么还有个变量没有解释呢?别着急,想知道另一个变量的作用,需要看另一个函数的代码,那就是sendBroadcast函数。从下面的代码注释中,可以看出sendBroadcast函数的作用就是从前面介绍的mActions两个集合中取出需要通知的广播接收者,使用 BroadcastRecord 进行记录,并触发执行executePendingBroadcasts函数罢了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 发送广播函数
public boolean sendBroadcast(@NonNull Intent intent) {
    // 省略代码:标识1 根据Intent中的信息从mActions中
    if (receivers != null) {
        // 省略代码
        /** 标识2:将发送的Intent和所有需要接收这个Intent的广播接收者封装为BroadcastRecord,并将其存放到mPendingBroadcasts中*/
        this.mPendingBroadcasts.add(new LocalBroadcastManager.BroadcastRecord(intent, receivers));
        /** 标识3:发送了一个Handler消息,追踪后发现,最后调用了executePendingBroadcasts函数 */
        if (!this.mHandler.hasMessages(1)) {
            this.mHandler.sendEmptyMessage(1);
        }
        return true;
    }
    // 省略代码
}

executePendingBroadcasts从函数名和前面的逻辑就可以猜到这个函数肯定是通知广播接收器接收Intent,并执行各自操作的最终函数。研究下面这个函数的代码后发现我们的猜测果然没有错,函数就是通过循环调用广播接收器的回调函数来实现 _广播_ 的功能的。什么本地广播管理器嘛,名头这么大,原来本质上就是通过普通的回调实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void executePendingBroadcasts() {
    // 注意这里是死循环
    while(true) {
        LocalBroadcastManager.BroadcastRecord[] brs;
        synchronized(this.mReceivers) {
            int N = this.mPendingBroadcasts.size();
            if (N <= 0) {
                // 当所有的广播接收器都通知到后,跳出死循环
                return;
            }
            // 取出所有的BroadcastRecord存放到临时列表中,并清空原列表
            brs = new LocalBroadcastManager.BroadcastRecord[N];
            this.mPendingBroadcasts.toArray(brs);
            this.mPendingBroadcasts.clear();
        }
        // 循环临时列表调用广播接收者的onReceive函数
        for(int i = 0; i < brs.length; ++i) {
            LocalBroadcastManager.BroadcastRecord br = brs[i];
            int nbr = br.receivers.size();

            for(int j = 0; j < nbr; ++j) {
                LocalBroadcastManager.ReceiverRecord rec = (LocalBroadcastManager.ReceiverRecord)br.receivers.get(j);
                if (!rec.dead) {
                    rec.receiver.onReceive(this.mAppContext, br.intent);
                }
            }
        }
    }
}

总结

前面通过分析LocalBroadcastManager的源码,我们对LocalBroadcastManager有了更深的理解。我们发现它并没有很复杂的逻辑实现,但却提供了很有用的功能。同时我们只是分析了它的大致逻辑实现原理,并没有对细节做分析,比如:synchronized关键字的使用,为什么通过Handler来触发执行函数?而executePendingBroadcasts函数中为什么要使用死循环来实现?这些细节就要自己去分析理解了,这样才能提高自己的代码水平。我当然已经想通了,哈哈~

LocalBroadcastManager封装

为了方便使用LocalBroadcastManager,我对LocalBroadcastManager进行了简单的封装,不仅有Java语言实现,还有Kotlin语言的实现。顺便说一下,Kotlin真好用!

Java实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;

/**
 * @Desc LocalBroadcastManager工具类
 * @Author wangwh
 * @Date 2019-03-14 15:06
 */
@SuppressWarnings("WeakerAccess")
public class LBMUtils {

    private LBMUtils() {
        throw new RuntimeException("Do not need instantiate!");
    }

    @NonNull
    public static LocalBroadcastManager getBroadcastManager(@NonNull Context ctx) {
        return LocalBroadcastManager.getInstance(ctx);
    }

    @Nullable
    public static IntentFilter getIntentFilter(@NonNull String... actions) {
        IntentFilter filter = null;
        if (actions.length > 0) {
            filter = new IntentFilter();
            for (String action : actions) {
                filter.addAction(action);
            }
        }
        return filter;
    }

    /**
     * @Desc 通过Action注册广播接收者
     * @Param [ctx, receiver, actions]
     */
    public static void registerReceiver(@NonNull Context ctx, @NonNull BroadcastReceiver receiver, @NonNull String... actions) {
        IntentFilter filter = getIntentFilter(actions);
        if (filter != null) {
            registerReceiver(ctx, receiver, filter);
        }
    }

    /**
     * @Desc 通过IntentFilter注册广播接收者
     * @Param [ctx, receiver, filter]
     */
    public static void registerReceiver(@NonNull Context ctx, @NonNull BroadcastReceiver receiver, @NonNull IntentFilter filter) {
        getBroadcastManager(ctx).registerReceiver(receiver, filter);
    }

    /**
     * @Desc 注销广播接收者
     * @Param [ctx, receiver]
     */
    public static void unRegisterReceiver(@NonNull Context ctx, BroadcastReceiver receiver) {
        getBroadcastManager(ctx).unregisterReceiver(receiver);
    }

    /**
     * @Desc 通过Action发送广播
     * @Param [ctx, action]
     */
    public static void sendBroadcast(@NonNull Context ctx, @NonNull String action) {
        sendBroadcast(ctx, new Intent(action));
    }

    /**
     * @Desc 通过intent发送广播
     * @Param [ctx, intent]
     */
    public static void sendBroadcast(@NonNull Context ctx, @NonNull Intent intent) {
        getBroadcastManager(ctx).sendBroadcast(intent);
    }

    /**
     * @Desc 通过Action同步发送广播
     * @Param [ctx, action]
     */
    public static void sendBroadcastSync(@NonNull Context ctx, @NonNull String action) {
        sendBroadcastSync(ctx, new Intent(action));
    }

    /**
     * @Desc 通过Intent同步发送广播
     * @Param [ctx, intent]
     */
    public static void sendBroadcastSync(@NonNull Context ctx, @NonNull Intent intent) {
        getBroadcastManager(ctx).sendBroadcastSync(intent);
    }
}

Kotlin实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@file:Suppress("NOTHING_TO_INLINE", "unused", "DEPRECATION", "SpellCheckingInspection")

import android.app.Fragment
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.support.v4.app.Fragment as FragmentV4
import android.support.v4.content.LocalBroadcastManager
import android.view.View
import org.jetbrains.anko.AnkoContext


/**
 * @Desc LocalBroadcastManager扩展 Kotlin API
 * @Author wangwh
 * @Date 2019-03-06 15:46
 */

fun getIntentFilter(vararg actions: String): IntentFilter {
    val filter = IntentFilter()
    for (action in actions) {
        filter.addAction(action)
    }
    return filter
}

// Context 的扩展实现
inline fun Context.unRegisterReceiver(receiver: BroadcastReceiver) = getBroadcastManager().unregisterReceiver(receiver)
inline fun Context.getBroadcastManager(): LocalBroadcastManager = LocalBroadcastManager.getInstance(this)
inline fun Context.sendBroadcast(action: String) = sendBroadcast(Intent(action))
inline fun Context.sendBroadcastSync(intent: Intent) = getBroadcastManager().sendBroadcastSync(intent)

// 同上实现,对 AnkoContext 进行扩展
inline fun AnkoContext<*>.registerReceiver(receiver: BroadcastReceiver, filter: IntentFilter) =
    ctx.registerReceiver(receiver, filter)
inline fun AnkoContext<*>.unRegisterReceiver(receiver: BroadcastReceiver) = ctx.unRegisterReceiver(receiver)
inline fun AnkoContext<*>.sendBroadcast(action: String) = ctx.sendBroadcast(action)
inline fun AnkoContext<*>.sendBroadcast(intent: Intent) = ctx.sendBroadcast(intent)
inline fun AnkoContext<*>.sendBroadcastSync(action: String) = ctx.sendBroadcast(action)
inline fun AnkoContext<*>.sendBroadcastSync(intent: Intent) = ctx.sendBroadcastSync(intent)

// 同上实现,对 View 进行扩展
inline fun View.registerReceiver(receiver: BroadcastReceiver, filter: IntentFilter) =
    context.registerReceiver(receiver, filter)
inline fun View.unRegisterReceiver(receiver: BroadcastReceiver) = context.unRegisterReceiver(receiver)
inline fun View.sendBroadcast(action: String) = context.sendBroadcast(action)
inline fun View.sendBroadcast(intent: Intent) = context.sendBroadcast(intent)
inline fun View.sendBroadcastSync(action: String) = context.sendBroadcast(action)
inline fun View.sendBroadcastSync(intent: Intent) = context.sendBroadcastSync(intent)

// 同上实现,对 Fragment 进行扩展
inline fun Fragment.registerReceiver(receiver: BroadcastReceiver, filter: IntentFilter) =
    activity.registerReceiver(receiver, filter)
inline fun Fragment.unRegisterReceiver(receiver: BroadcastReceiver) = activity.unRegisterReceiver(receiver)
inline fun Fragment.sendBroadcast(action: String) = activity.sendBroadcast(action)
inline fun Fragment.sendBroadcast(intent: Intent) = activity.sendBroadcast(intent)
inline fun Fragment.sendBroadcastSync(action: String) = activity.sendBroadcast(action)
inline fun Fragment.sendBroadcastSync(intent: Intent) = activity.sendBroadcastSync(intent)

// 同上实现,对 FragmentV4 进行扩展
inline fun FragmentV4.registerReceiver(receiver: BroadcastReceiver, filter: IntentFilter) =
    requireActivity().registerReceiver(receiver, filter)
inline fun FragmentV4.unRegisterReceiver(receiver: BroadcastReceiver) = requireActivity().unRegisterReceiver(receiver)
inline fun FragmentV4.sendBroadcast(action: String) = requireActivity().sendBroadcast(action)
inline fun FragmentV4.sendBroadcast(intent: Intent) = requireActivity().sendBroadcast(intent)
inline fun FragmentV4.sendBroadcastSync(action: String) = requireActivity().sendBroadcast(action)
inline fun FragmentV4.sendBroadcastSync(intent: Intent) = requireActivity().sendBroadcastSync(intent)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值