安卓broadcastreceiver_Android手电筒原理

安卓broadcastreceiver_Android手电筒原理目录BroadcastReceiver概述BroadcastReceiver分类registerReceiver流程图源码解析总结1.BroadcastReceiver概述广播作为四大组件之一,在平时开发过程中会大量使用到,使用方式也是多种多样的,既可以自己在manifest中注册,也可以在java代码中动态注册,既可以接收由系统发出的广播,也可以接受自己定义并发送的广播。广播…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

目录

  • BroadcastReceiver概述
  • BroadcastReceiver分类
  • registerReceiver流程图
  • 源码解析
  • 总结

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,这也就是后面广播的派发了。
从设计模式的角度讲,这正是经典的观察者模式。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/194660.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • EXTJS详细教程

    EXTJS详细教程布局和容器普通布局Ext.create(‘Ext.panel.Panel’,{renderTo:Ext.getBody(),width:400,height:300,title:’ContainerPanel’,items:[{xtype:’panel’,title:’ChildPanel1′,height:100,width:’75%’},{

  • 时间倒计时代码

    简单易用的倒计时js代码-站长素材*{margin:0;padding:0;list-style:none;}body{font-size:18px;text-align:center;}.time{height:30px;padding:200px;}          00天       00时       00分   

  • micropython教程(Python集成开发环境)

    本文旨在通过一个简单的demo,介绍基于Python3、PyQT5的环境下开发桌面应用程序的一种方案,当然开发Python的桌面应用程序不止是PyQT这一种方案,还可以使用Python自带的Tkinter来实现。本文目录:1.安装依赖环境2.安装Eric63.配置Eric4.创建窗口应用4.1创建窗体UI4.2实现代码逻辑参考资料:1.安装依赖环境Eric6官网:htt…

  • java命令行执行 jar_java命令打包jar

    java命令行执行 jar_java命令打包jar摘要这个技巧阐明了如何不直接处理清单文件而将一个不能运行jar包转换成一个可以执行的jar包。学会如何写一段转换jar包的程序,将你的jar包转换成你能使用java-jar命令运行jar包或象在windows系统上那样通过双击来运行jar包。你可以很容易地将一个应用的所有的类和资源打包到一个jar文件中去。事实上,这只是打包的一个原因。另一个原因是让用户很容易地执行包中的应用。那么在java的…

  • 面试必备之乐观锁与悲观锁

    面试必备之乐观锁与悲观锁推荐阅读:如何在技术领域持续成长后端程序员必备的Linux基础知识后端必备——数据通信知识(RPC、消息队列)一站式总结何谓悲观锁与乐观锁乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人。悲观锁总是假设最坏的情况,每次去拿数据的时候都认为…

  • Hybrid开发_什么是移动端开发

    Hybrid开发_什么是移动端开发一、移动应用开发的方式1、目前主要有三种(如上图):NativeApp:本地应用程序(原生App)WebApp:网页应用程序(移动web)HybridApp:混合应用程序(混合App)nativeapp是原生安卓或ios程序员写的手机app。hybridapp是混合app。可以是安卓或iosf程序员写一部分,然后前端写html代码,把html代码给安卓或ios嵌套进去;也可以是直接的html网站,将其打包,嵌套一个app壳(在壳里面,其实就是只做了一个内嵌浏览器)。

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号