返回顶部
首页 > 资讯 > 移动开发 >Android Broadcast原理分析之registerReceiver详解
  • 146
分享到

Android Broadcast原理分析之registerReceiver详解

2024-04-02 19:04:59 146人浏览 泡泡鱼
摘要

目录1. BroadcastReceiver概述2. BroadcastReceiver分类3. reGISterReceiver流程图4. 源码解析4.1 ContextImpl.

1. BroadcastReceiver概述

广播作为四大组件之一,在平时开发过程中会大量使用到,使用方式也是多种多样的,既可以自己在manifest中注册,也可以在java代码中动态注册,既可以接收由系统发出的广播,也可以接受自己定义并发送的广播。广播可以实现进程内以及跨进程之间的通信。从本文开始将分别介绍广播的注册,广播的派发,本地广播(LocalBroadcast)以及Android O上对广播的限制,本文主要介绍广播动态注册。

2. BroadcastReceiver分类

从注册方式上区分:动态注册以及静态注册(显示广播和隐式广播)
从发送方式上区分:无序广播和有序广播
从处理类型上区分:前台广播和后台广播
从运行方式上区分:普通广播和Sticky广播(已弃用)
从发送者区分:系统广播和自定义广播
此外还有protect broadcast(只允许指定应用可以发送)
sticky广播:系统保存了一部分广播的状态,当你注册的时候,不需要等到下次这个广播发出来,直接把最近上一次发送的这个广播返回给你

以上的这些概念在接下来的介绍中都会逐个涉及。

3. registerReceiver流程图

registerReceiver

其中的APP,ContextImpl,LoadedApk,ActivityManagerProxy都在APP本身的进程中,ActivityManagerService在system_server进程中。

  1. 首先在APP的进程中发起广播的注册,通过registerReceiver接口,这个方法有很多重载方法,但是最终的入口都是在ContextImpl中,后面会详细介绍
  2. 从之前的Context的学习可以知道,registerReceiver最终调用的实现在ContextImpl
  3. 如果没有指定处理广播的handler,则默认使用主线程的handler
  4. 获取要注册的ReceiverDispatcher,在注册的Context相同的情况下,每个Receiver对应一个ReceiverDispatcher
  5. 通过binder call到systemserver进行广播注册

4. 源码解析

4.1 ContextImpl.registerReceiverInternal


    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) {
                    // 注册receiver的时候可以指定接受recover的Handler
                    // 如果没有指定,则默认用主线程的handler处理
                    scheduler = mMainThread.getHandler();
                }
                // 获取IIntentReceiver
                // 这个是一个Binder对象,当广播来临时,用于AMS向客户端发起回调
                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 {
            // binder call至AMS,进行广播注册
            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();
        }
    }

参数解析:

receiver:将要注册的receiver
userId:用户空间标志,默认情况下我们都只有一个user,现在一些手机推出的分身,其实就是用的第二个user,这种情况下userid会变,否则默认主空间的都是0
IntentFilter:要注册的广播的filter
broadcastPermission:指定要注册的广播的权限
scheduler:指定广播接受(也就是onReceive)所在的线程,也就是说注册的时候就可以指定好广播处理放在哪个线程,如果receiver中事情太多,可以放在另外一个线程,这样可以避免主线程被卡住
context:通过getOuterContext获取到,前面在介绍context的时候有提到,application/service/activity中获取到的是不一样的
flags:注册广播所携带的flag

4.2 LoadedApk.getReceiverDispatcher


    public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
            Context context, Handler handler,
            Instrumentation instrumentation, boolean registered) {
        synchronized (mReceivers) {
            // 如果Context相同,每个receiver对应一个ReceiverDispatcher
            LoadedApk.ReceiverDispatcher rd = null;
            ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
            if (registered) {
                map = mReceivers.get(context);
                if (map != null) {
                    rd = map.get(r);
                }
            }
            if (rd == null) {
                rd = new ReceiverDispatcher(r, context, handler,
                        instrumentation, registered);
                if (registered) {
                    if (map == null) {
                        map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                        mReceivers.put(context, map);
                    }
                    map.put(r, rd);
                }
            } else {
                rd.validate(context, handler);
            }
            rd.mForGotten = false;
            return rd.getIIntentReceiver();
        }
    }

mReceivers是一个二级map,一级key是context,二级key是BroadcastReceiver,value是ReceiverDispatcher。

这里的ReceiverDispatcher又是什么呢?

它是LoadedApk中的一个内部类,保存了这个receiver的信息,用于在广播派发到本进程的时候执行,上面这方法最重要的是getIIntentReceiver,这个就非常重要了,它是一个Binder对象,说在广播注册的时候将这个binder对象传递到了AMS,然后当广播派发到本进程的时候,通过这个binder对象再会调回来,它在ReceiverDispatcher创建的时候创建。


    static final class ReceiverDispatcher {
        // 是一个binder对象
        final static class InnerReceiver extends IIntentReceiver.Stub {
            final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
            final LoadedApk.ReceiverDispatcher mStrongRef;

            InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
                mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
                mStrongRef = strong ? rd : null;
            }

            @Override
            public void perfORMReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                    // 这里就是广播真正派发到本进程的时候从systemserver binder call过来执行的
                ...
        }

        final IIntentReceiver.Stub mIIntentReceiver;
        final BroadcastReceiver mReceiver;
        final Context mContext;
        final Handler MactivityThread;
        final Instrumentation mInstrumentation;
        final boolean mRegistered;
        final IntentReceiverLeaked mLocation;
        RuntimeException mUnregisterLocation;
        boolean mForgotten;
        ...
    }

到这里,广播注册在APP进程的流程就走完了,主要做了几件事:

  1. 获取handler
  2. 获取ReceiverDispatcher
  3. 获取InnerReceiver
  4. 将上面这些连带receiver的相关信息,发起binder call到ActivityManagerService

4.3 ActivityManagerService.registerReceiver


    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) {
        enforceNotIsolatedCaller("registerReceiver");
        ArrayList<Intent> stickyIntents = null;
        ProcessRecord callerApp = null;
        final boolean visibleToInstantApps
                = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
        int callingUid;
        int callingPid;
        boolean instantApp;
        synchronized(this) {
            if (caller != null) {
                // 正常来讲caller是发起binder call的客户端进程对应的ApplicationThread对象
                // 如果为null则抛异常
                callerApp = getRecordForAppLocked(caller);
                if (callerApp == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                            + " (pid=" + Binder.getCallingPid()
                            + ") when registering receiver " + receiver);
                }
                if (callerApp.info.uid != SYSTEM_UID &&
                        !callerApp.pkgList.containsKey(callerPackage) &&
                        !"android".equals(callerPackage)) {
                    throw new SecurityException("Given caller package " + callerPackage
                            + " is not running in process " + callerApp);
                }
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
            } else {
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }

            // 判断caller是否为instant app
            instantApp = isInstantApp(callerApp, callerPackage, callingUid);
            userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                    ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

            // 获取广播注册的filter中的action封装到list中
            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }

            // mStickyBroadcasts是一个二级map
            // 一级key是userId,二级key是广播对应的action,value是广播对应intent的list(一般只有一个intent)
            // 这里是为了查询对于当前user,本次注册的所有action对应的sticky广播的intent
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
            while (actions.hasNext()) {
                String action = actions.next();
                for (int id : userIds) {
                    ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                    if (stickies != null) {
                        ArrayList<Intent> intents = stickies.get(action);
                        if (intents != null) {
                            if (stickyIntents == null) {
                                stickyIntents = new ArrayList<Intent>();
                            }
                            stickyIntents.addAll(intents);
                        }
                    }
                }
            }
        }

        ArrayList<Intent> allSticky = null;
        // 这里不为null表示本次注册的广播中有sticky广播
        if (stickyIntents != null) {
            final ContentResolver resolver = mContext.getContentResolver();
            // 查找匹配的sticky广播
            for (int i = 0, N = stickyIntents.size(); i < N; i++) {
                Intent intent = stickyIntents.get(i);
                // 如果caller是instant app,且intent的flag不允许对instant可见,则跳过
                if (instantApp &&
                        (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
                    continue;
                }
                // If intent has scheme "content", it will need to acccess
                // provider that needs to lock mProviderMap in ActivityThread
                // and also it may need to wait application response, so we
                // cannot lock ActivityManagerService here.
                if (filter.match(resolver, intent, true, TAG) >= 0) {
                    if (allSticky == null) {
                        allSticky = new ArrayList<Intent>();
                    }
                    allSticky.add(intent);
                }
            }
        }

        // 直接把最近的一个匹配到的sticky广播返回
        Intent sticky = allSticky != null ? allSticky.get(0) : null;
        // 广播注册的时候receiver是可以为null的,这种情况下这里直接return
        if (receiver == null) {
            return sticky;
        }

        synchronized (this) {
            // 校验caller进程是否正常
            if (callerApp != null && (callerApp.thread == null
                    || callerApp.thread.asBinder() != caller.asBinder())) {
                // Original caller already died
                return null;
            }
            // mRegisteredReceivers中存放了所有的已注册的receiver
            // 每个BroadcastReceiver对应一个InnerReceiver,即Binder对象
            // binder对象做key,value是ReceiverList
            // ReceiverList是一个ArrayList
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    rl.app.receivers.add(rl);
                } else {
                    try {
                        // 如果是新创建的receiver,还需要linkToDeath
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
                // 放入mRegisteredReceivers
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            } else if (rl.uid != callingUid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for uid " + callingUid
                        + " was previously registered for uid " + rl.uid
                        + " callerPackage is " + callerPackage);
            } else if (rl.pid != callingPid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for pid " + callingPid
                        + " was previously registered for pid " + rl.pid
                        + " callerPackage is " + callerPackage);
            } else if (rl.userId != userId) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for user " + userId
                        + " was previously registered for user " + rl.userId
                        + " callerPackage is " + callerPackage);
            }
            // 每一个IntentFilter对应一个BroadcastFilter
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            // receiverList中存放了通过这个receiver注册的所有的filter
            // 每调用一次register就会add一次
            rl.add(bf);
            // mReceiverResolver中存放所有的BroadcastFilter
            mReceiverResolver.addFilter(bf);

            // 有匹配的sticky广播,则直接开始调度派发
            if (allSticky != null) {
                ArrayList receivers = new ArrayList();
                receivers.add(bf);

                // 对于每一个sticky广播,创建BroadcastRecord并入队(并行)
                final int stickyCount = allSticky.size();
                for (int i = 0; i < stickyCount; i++) {
                    Intent intent = allSticky.get(i);
                    // 根据flag是否有FLAG_RECEIVER_FOREGROUND判断入队是前台还是后台队列
                    BroadcastQueue queue = broadcastQueueForIntent(intent);
                    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                            null, -1, -1, false, null, null, AppOpsManager.OP_NONE, null, receivers,
                            null, 0, null, null, false, true, true, -1);
                    // 入队,并行队列
                    queue.enqueueParallelBroadcastLocked(r);
                    // 启动广播的调度,也就是开始派发广播
                    queue.scheduleBroadcastsLocked();
                }
            }
            return sticky;
        }
    }

上面主要做了几件事情:

  1. 对caller的判断
  2. 遍历action,查询是否有匹配的sticky广播
  3. 将本次注册的广播放到mRegisteredReceivers中记录
  4. 如果是sticky广播,开始派发

5. 总结

本文从App的一次广播注册发起开始,到systemserver的注册流程,整体上流程还是比较简单的,顾名思义,注册,正是把要注册的广播在systemserver中进行登记,等到这个事件真正来临的时候,从登记中取出需要被通知的receiver,这也就是后面广播的派发了。
设计模式的角度讲,这正是经典的观察者模式。

到此这篇关于Android Broadcast原理分析之registerReceiver详解的文章就介绍到这了,更多相关Android Broadcast原理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Android Broadcast原理分析之registerReceiver详解

本文链接: https://lsjlt.com/news/133608.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Android Broadcast原理分析之registerReceiver详解
    目录1. BroadcastReceiver概述2. BroadcastReceiver分类3. registerReceiver流程图4. 源码解析4.1 ContextImpl....
    99+
    2024-04-02
  • Android基础知识之broadcast广播详解
    Android中的广播用的太多了,今天稍微总结一下。 按注册方式分为两种: 1.静态注册广播: 静态注册广播就是在androidManifest.xml文件中注册广播,假设...
    99+
    2022-06-06
    Android
  • Android四大组件之broadcast广播详解
    目录基础广播两大类广播广播的动态和静态注册监听时间变化监听系统启动案例创建 receiver设置权限发送自定义广播如何发送与接收有序广播基础广播 两大类广播 标准广播:异步执行,广播...
    99+
    2024-04-02
  • Android ANR原理分析
    目录卡顿原理 卡顿监控 ANR原理 卡顿原理 主线程有耗时操作会导致卡顿,卡顿超过阀值,触发ANR。 应用进程启动时候,Zygote会反射调用ActivityThread的main...
    99+
    2024-04-02
  • Android mvvm之LiveData原理案例详解
    1. 生命周期感知 1.1 生命周期感知组件 我们知道,Controller(Activity or Fragment) 都是有生命周期的,但是传统的 Controller 实现方式...
    99+
    2024-04-02
  • 详解Android JetPack之LiveData的工作原理
    目录前言 介绍 原理分析 前言 本篇文章主要讲解LiveData工作的原理,如果还不知道LiveData如何用的话,请参考官方文档。 LiveData的讲解涉及到了Lifecycl...
    99+
    2024-04-02
  • Java CompletableFuture实现原理分析详解
    目录简介CompletableFuture类结构CompletableFuture回调原理CompletableFuture异步原理总结简介 前面的一篇文章你知道Java8并发新特性...
    99+
    2024-04-02
  • MySQL读写分离原理详细解析
    目录一、读写分离的概念二、引入中间件MyCat三、MyCat服务端口和管理端口一、读写分离的概念 读写分离是基于主从复制来实现的。在实际的应用环境中,肯定是读操作多,就像我们在电商平台上去购买东西,可能看了100个也就买...
    99+
    2022-07-14
    MySQL读写分离原理 MySQL读写分离
  • JSP 中Session的详解及原理分析
    JSP(JavaServer Pages)中的Session是一种用于在服务器端存储和传递用户数据的机制。它可以在一个用户的多个请求之间保持数据的一致性,并且允许用户在不同的页面之间共享数据。下面是Session的详解和原理分析:Ses...
    99+
    2023-08-09
    js
  • 关于Android触摸事件分发的原理详析
    目录一:前言二:说在前面的知识三:整体流程1:activity2:window就是PhoneWindow3:view group4:view四:一些关键点五:从源码看触摸事件分发总结...
    99+
    2024-04-02
  • Android APK 签名打包原理分析(二)【Android签名原理】
    说到签名,从这个词来理解,正常个人需要签名的时候,一般是用来证明这是某个人的特属认证。 大家是否有印象?还记得我们之前在学习、总结网络相关知识的时候,说到过,客户端和服务端虽然通信数据上,可以采用对称加密和非对称加密组合去进行数据的加密...
    99+
    2023-08-16
    android 签名 打包 消息摘要
  • jvm原理之SystemGC源码分析
    概述 JVM的GC一般情况下是JVM本身根据一定的条件触发的,不过我们还是可以做一些人为的触发,比如通过jvmti做强制GC,通过System.gc触发,还可以通过jmap来触发等,...
    99+
    2024-04-02
  • Android LayoutInflater.inflate()详解及分析
    Android  LayoutInflater.inflate()详解 深入理解LayoutInflater.inflate() 由于我们很容易习惯公式化的预置代码...
    99+
    2022-06-06
    layoutinflater Android
  • SpringBoot自动装配的原理详解分析
    目录前言自动装配案例自动装配分析自动装配总结前言 关于 ​​SpringBoot​​​ 的自动装配功能,相信是每一个 ​​Java​​ 程序员...
    99+
    2022-11-13
    SpringBoot 自动装配原理 SpringBoot 自动装配
  • Java@Autowired注解底层原理详细分析
    目录1.概念2.注入数据的注解3.@Autowired注解是如何实现的1.概念 @Autowired 是 Spring 提供的注解,默认的注入方式为 byType (按类型自动注入)...
    99+
    2022-11-13
    Java @Autowired注解 Java @Autowired Java @Autowired原理
  • Android Handler工作原理解析
    简介 在Android 中,只有主线程才能操作 UI,但是主线程不能进行耗时操作,否则会阻塞线程,产生 ANR 异常,所以常常把耗时操作放到其它子线程进行。如果在子线程中需要...
    99+
    2022-06-06
    handler Android
  • Android开发Composeremember原理解析
    目录正文随机色文本原因分析正确实现remember的原理剖析小结正文 看过Compose案例或者源码的你,相信肯定是见过 remember 了的。顾名思义,Compose是要让我们的...
    99+
    2024-04-02
  • Android 推送原理(Android Push Notification)详解
    Android 推送原理 由于最近项目中涉及到了Android推送,所以看了一些关于android推送方面的东西,总结到后面我们知道android推送的实现可以归结为3种: ...
    99+
    2022-06-06
    notification Android
  • Kotlin原理详析之拓展函数
    目录原理限制不能访问私有成员拓展函数不能实现多态成员函数优先级高,拓展函数不能实现重写为什么要使用Kotlin中的扩展函数总结原理 拓展函数是kotlin里一个比较常用的特性,例如我...
    99+
    2024-04-02
  • Redis集群原理详细分析
    目录一致性哈希Redis 集群一致性哈希 节点的增加和减少,大部分节点的 Hash一致 package consistenthash import ( "hash/crc32" ...
    99+
    2022-12-19
    Redis集群 Redis集群原理 Redis集群模式
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作