LocalBroadcastManager-让你的app广播更安全-源码解读

LocalBroadcastManager-让你的app广播更安全-源码解读

LocalBroadcastManager:属于V4
我们都知道android的广播及其广播机制;但是当我们的app签名打包后,在清单文件AndroidManifest.xml中配置的广播就可能泄漏,从而可能被利用….

先复习一下:android的系统广播一般分类2类

无序广播

**sendBroadcast(Intent intent);**
    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.  No results are propagated from
    receivers and receivers can not abort the broadcast. If you want
    to allow receivers to propagate results or abort the broadcast, you must
    send an ordered broadcast using sendOrderedBroadcast(Intent, String)
上面是源码注释:将intent发送给所有对应的广播接受者,这个方法是**异步**执行的,
    广播接受者不会返回任何结果
    注册了该广播的接受者不能跳过该广播,也就是一定会接收到
如果需要从广播接受者中返回结果,那么就需要使用有序广播

有序广播

**sendOrderedBroadcast(Intent intent)**
    Broadcast the given intent to all interested BroadcastReceivers, delivering
    them one at a time to allow more preferred receivers to consume the
    broadcast before it is delivered to less preferred receivers.  This
    call is asynchronous; it returns immediately, and you will continue
    executing while the receivers are run.
   有序广播执行也是异步的,和主线程无关的~~
   这个有序广播有多个重载的方法:
   比如:

sendOrderedBroadcast(@NonNull Intent intent,
@Nullable String receiverPermission, BroadcastReceiver resultReceiver,
@Nullable Handler scheduler, int initialCode, @Nullable String initialData,
@Nullable Bundle initialExtras);
这个方法就是指定了接受者的接受权限,和最后的返回结果的接受者这个有序广播就是比较强有力的广播了,如果没有指定最后的广播接受者,那么有序广播就有可能被中间的广播接受者拦截掉:就很类似中央下发的一些文件,这些文件可能会被某些地方官员给拦截,不让底层人们知道~~
所以,当我们的app被打包出去后,被反编译获取了清单文件中的一些广播数据,那么有心人就可以在代码中注册一个广播,通过设置优先级(优先级在IntentFilter这个类的对象的setPriority方法设置:)

/**
     * Modify priority of this filter.  The default priority is 0. Positive
     * values will be before the default, lower values will be after it.
     * Applications must use a value that is larger than
     * {@link #SYSTEM_LOW_PRIORITY} and smaller than
     * {@link #SYSTEM_HIGH_PRIORITY} .
     *
     * @param priority The new priority value.
     *
     * @see #getPriority
     * @see #SYSTEM_LOW_PRIORITY
     * @see #SYSTEM_HIGH_PRIORITY
     */
    public final void setPriority(int priority) {
        mPriority = priority;
    }

可以看到这个广播的设置优先级,默认是0,实际上说是最大值只有SYSTEM_HIGH_PRIORITY(查看源码是1000),最高值可以有 Integer.MAX_VALUE;所以当使用的是代码注册,而且优先级又设置为最大值的时候,这个人就可以对这个广播为所欲为了~~~

回归正题:LocalBroadcastManager怎样让app的广播更加安全

LocalBroadcastManager

Helper to register for and send broadcasts of Intents to local objects
within your process

本地广播管理器,只会在你的进程里(也就是你的app里)注册和发送广播,这样就不用担心自己的广播以及广播中的数据被其他app或有心人获取到,从而泄漏了重要的数据:
下面我们分析一下它的源码:这是一个单例类

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

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

进程内广播注册

其广播注册和系统广播注册完全一样,只不过系统广播注册调用的是Context的registerReceiver方法,这里注册需要使用LocalBroadcastManager的注册方法:

 /**
     * Register a receive for any local broadcasts that match the given IntentFilter.
     *
     * @param receiver The BroadcastReceiver to handle the broadcast.
     * @param filter Selects the Intent broadcasts to be received.
     *
     * @see #unregisterReceiver
     */
    public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mReceivers) {
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            ArrayList<IntentFilter> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<IntentFilter>(1);
                mReceivers.put(receiver, filters);
            }
            filters.add(filter);
            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);
            }
        }
    }

方法注册的时候,显示生成了一个对象ReceiverRecord,记录广播接受者的一个内部类;

 private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
            = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();

然后从mReceivers(保存广播接受者和对应的过滤器的集合)中获取过滤器,如果没有过滤器集合(filters),那么new一个出来,然后放入mReceivers集合中
然后将filter添加到filters集合中,之后遍历这个filter所有action将action添加到mActions集合中(这个集合是onreceive的时候使用,可以通过action获取ReceiverRecord,然后调用ReceiverRecord的onreceive方法~~,这就是收到广播了)

private final HashMap<String, ArrayList<ReceiverRecord>> mActions
            = new HashMap<String, ArrayList<ReceiverRecord>>();

当吧ReceiverRecord和action添加到对应的集合中的时候,注册就完成了。
下面看发送广播

进程内发送广播

 /**
     * 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.
     *
     * @param intent The Intent to broadcast; all receivers matching this
     *     Intent will receive the broadcast.
     *
     * @see #registerReceiver
     */
    public boolean sendBroadcast(Intent intent) {
        synchronized (mReceivers) {
           ......
            /**
            *这里就是通过action获取在mActions里注册过的广播接收器的集合
            */
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            if (entries != null) {
                ArrayList<ReceiverRecord> receivers = null;
                for (int i=0; i<entries.size(); i++) {//遍历广播接收器的集合,获取对应的接受者
                    ReceiverRecord receiver = entries.get(i);
                    if (receiver.broadcasting) {
                        if (debug) {
                            Log.v(TAG, "  Filter's target already added");
                        }
                        continue;
                    }
                    //获取符合过滤器规则的广播接收器
                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");
                    if (match >= 0) {
                            if (receivers == null) {
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        receivers.add(receiver);
                        receiver.broadcasting = true;
                    } else {
                      ........
                    }
                }
                //然后执行
                if (receivers != null) {
                    for (int i=0; i<receivers.size(); 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;
                }
            }
        }
        return false;
    }

这里就是依据action从容器mActions里获取ReceiverRecord,然后判断ReceiverRecord是否是在广播状态(broadcasting),然后将这个ReceiverRecord 添加到容器mPendingBroadcasts(存储需要执行的广播接受者)中
最后是通过handler发送一个message去执行这个广播了:handler里调用的是下面这个方法:这个就是真正的调用注册广播的onreceive的方法:里面的逻辑倒是比较简单,从需要执行的缓存容器中取出receiver对象,然后执行onReceiver方法:从这里看,实际上这个广播管理器也是顺序执行的,只不过这个onReceiver方法执行的很快而已,这难道也是onReceive里不能执行耗时操作的原因?

 private void executePendingBroadcasts() {
        while (true) {
            BroadcastRecord[] brs = null;
            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++) {
                BroadcastRecord br = brs[i];
                for (int j=0; j<br.receivers.size(); j++) {
                    br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
                }
            }
        }
    }

基本上,读到这里,你也可以写一个自己的广播管理器了。
补充:BroadcastRecord 是LocalBroadcastManager的一个内部类,代码如下,很简单咯~~~

 private static class BroadcastRecord {
        final Intent intent;
        final ArrayList<ReceiverRecord> receivers;

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

使用LocalBroadcastManager需要注意的:

和系统广播一样,一定要注意:注册了广播,要注销广播
否则,在LocalBroadcastManager里使用都是强引用,当在某个activity内注册了这个广播,那么这个activity 在destroy的时候就有可能发生内存泄漏,但这个类并不像Context那样,会给你提醒内存泄漏。
还有可能造成的影响就是:多次进出这个activity后,会注册N个广播,然后发一个广播,onReceive会执行N次,如果onReceive里执行的是请求数据操作那么担心担心流量~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
uni-app是一个跨平台的开发框架,可以实现一次开发,多端发布的效果。能够利用uni-app,我们可以方便地仿制今日头条新闻app。 首先,我们可以通过uni-app的页面结构和组件库来搭建类似今日头条的界面,括顶部的导航栏、新闻列表的展示等。 然后,我们可以通过uni-app框架的网络请求功能,获取今日头条新闻的数据,并将其展示在界面上。可以利用uni-app内置的request或者axios等库来发送HTTP请求,并获取返回的新闻数据。 对于新闻列表展示,我们可以利用uni-app的列表渲染功能,将获取到的新闻数据渲染到页面上。同时,可以使用uni-app的下拉刷新组件实现新闻的实时新。 另外,可以利用uni-app的路由功能,实现新闻详情页的跳转。当用户点击某个新闻标题时,可以将对应的新闻ID传递给详情页,并通过uni-app的路由功能进行页面的跳转。 在详情页中,可以展示新闻的详细内容,并可以提供评论、点赞等交互功能。可以利用uni-app内置的组件库,实现这些功能。 最后,针对用户的个人设置和喜好,可以通过uni-app的本地存储功能,实现收藏、关注等功能。用户可以自主选择感兴趣的栏目,并将其保存在本地,方便下次打开app时快速浏览相关内容。 总之,利用uni-app框架,我们可以轻松实现仿制今日头条新闻app源码。通过运用uni-app的丰富功能和组件库,可以实现新闻的展示、跳转、交互等各种特性,以及个性化设置和存储功能。无论是在IOS、Android还是其他平台上,利用uni-app都能实现一次开发,多端发布,提高开发效率,降低开发成本。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值