Binder与 四大组件工作原理 Service、BroadCastReceiver、ContentProvider

news2025/2/25 5:55:45

Service 工作原理

Service有两套流程,一套是启动流程,另一套是绑定流程。我们做App开发的同学都应该知道

在这里插入图片描述

1)在新进程启动Service

我们先看Service启动过程,假设要启动的Service是在一个新的进程中,分为5个阶段:

  1. App向AMS发送一个启动Service的消息。
  2. AMS检查启动Service的进程是否存在,如果不存在,先把Service信息存下来,然后创建一个新的进程。
  3. 新进程启动后,通知AMS说我可以啦。
  4. AMS把刚才保存的Service信息发送给新进程
  5. 新进程启动Service
第1阶段

在这里插入图片描述
和Activity非常像,仍然是通过AMM/AMP把要启动的Service信息发送给AMS。

第2阶段
  1. AMS检查Service是否在Manifest中声明了,没声明会直接报错。

  2. AMS检查启动Service的进程是否存在,如果不存在,先把Service信息存下来,然后创建一个新的进程。

  3. 在AMS中,每个Service,都使用ServiceRecord对象来保存。

第3阶段

Service所在的新进程启动的过程,就和前面介绍App启动时的过程差不多。

新进程启动后,也会创建新的ActivityThread,然后把ActivityThread对象通过AMP传递给AMS,告诉AMS,新进程启动成功了。

第4阶段

AMS把传进来的ActivityThread对象改造为ApplicationThreadProxy,也就是ATP,通过ATP,把要启动的Service信息发送给新进程。

第5阶段

在这里插入图片描述
新进程通过ApplicationThread接收到AMS的信息,和前面介绍的启动Activity的最后一步相同,借助于ActivityThread和H,执行Service的onCreate方法。在此期间,为Service创建了Context上下文对象,并与Service相关联。

需要重点关注的是ActivityThread的handleCreateService方法,
在这里插入图片描述

你会发现,这段代码和前面介绍的handleLaunchActivity差不多,都是从PMS中取出包的信息packageInfo,这是一个LoadedApk对象,然后获取它的classloader,反射出来一个类的对象,在这里反射的是Service。

四大组件的逻辑都是如此,所以我们要做插件化,可以在这里做文章,换成插件的classloader,加载插件中的四大组件。

至此,我们在一个新的进程中启动了一个Service。

2)启动同一进程的Service

如果是在当前进程启动这个Service,那么上面的步骤就简化为:

  1. App向AMS发送一个启动Service的消息。

  2. AMS例行检查,比如Service是否声明了,把Service在AMS这边注册。AMS发现要启动的Service就是App所在的Service,就通知App启动这个Service。

  3. App启动Service。

3)在同一进程绑定Service

如果是在同一进程绑定这个Service,那么过程为:

  1. App向AMS发送一个绑定Service的消息。

  2. AMS例行检查,比如Service是否声明了,把Service在AMS这边注册。AMS发现要启动的Service就是App所在进程的Service,就先通知App启动这个Service,然后再通知App,对Service进行绑定操作。

  3. App收到AMS第1个消息,启动Service,

  4. App收到AMS第2个消息,绑定Service,并把一个Binder对象传给AMS

  5. AMS把接收到的Binder对象,发送给App

  6. App收到Binder对象,就可以使用了。

你也许会问,都在一个进程,App内部直接使用Binder对象不就好了,其实吧,要考虑不在一个进程的场景,代码又不能写两份,两套逻辑,所以就都放在一起了,即使在同一个进程,也要绕着AMS走一圈。

第1阶段:App向AMS发送一个绑定Service的消息。
在这里插入图片描述
第4阶段:处理第2个消息
在这里插入图片描述
第5阶段和第6阶段:
这一步是要仔细说的,因为AMS把Binder对象传给App,这里没用ATP和APT,而是用到了AIDL来实现,这个AIDL的名字是IServiceConnection。
在这里插入图片描述
ServiceDispatcher的connect方法,最终会调用ServiceConneciont的onServiceConnected方法,这个方法我们就很熟悉了。App开发人员在这个方法中拿到connection,就可以做自己的事情了。

BroadCast Receiver 工作原理

Receiver分静态广播和动态广播两种。

在Manifest中声明的Receiver,是静态广播:
在这里插入图片描述
在程序中手动写注册代码的,是动态广播:
在这里插入图片描述
二者具有相同的功能,只是写法不同。既然如此,我们就可以把所有静态广播,都改为动态广播,这就省的在Manifest文件中声明了,避免了AMS检查。接下来你想到什么?对,Receiver的插件化解决方案,就是这个思路。

接下来我们看Receiver是怎么和AMS打交道的,分为两部分,一是注册,二是发送广播。

你只有注册了这个广播,发送这个广播时,才能通知到你,执行onReceive方法。

我们就拿音乐播放器来举例,在Activity注册Receiver,在Service发送广播。Service播放下一首音乐时,会通知Activity修改当前正在播放的音乐名称。

注册过程如下所示:

  1. 在Activity中,注册Receiver,并通知AMS。
    在这里插入图片描述

这里Activity使用了Context提供的registerReceiver方法,然后通过AMN/AMP,把一个receiver传给AMS。

在创建这个Receiver对象的时候,需要为receiver指定IntentFilter,这个filter就是Receiver的身份证,用来描述receiver。

在Context的registerReceiver方法中,它会使用PMS获取到包的信息,也就是LoadedApk对象。

就是这个LoadedApk对象,它的getReceiverDispatcher方法,将Receiver封装成一个实现了IIntentReceiver接口的Binder对象。

我们就是将这个Binder对象和filter传递给AMS。

只传递Receiver给AMS是不够的,当发送广播时,AMS不知道该发给谁啊?所以Activity所在的进程还要把自身对象也发送给AMS。

  1. AMS收到消息后,就会把上面这些信息,存在一个列表中,这个列表中保存了所有的Receiver。

注意了,这里忙活半天,都是在注册动态receiver。
静态receiver什么时候注册到AMS的呢?是在App安装的时候。PMS会解析Manifest中的四大组件信息,把其中的receiver存起来。
动态receiver和静态receiver分别存在AMS不同的变量中,在发送广播的时候,会把两种receiver合并到一起,然后以此发送。其中动态的排在静态的前面,所以动态receiver永远优先于静态receiver收到消息。

此外,Android系统每次启动的时候,也会把静态广播接收者注册到AMS。因为Android系统每次启动时,都会重新安装所有的apk。

发送广播的流程如下:
  1. 在Service中,通过AMM/AMP,发送广播给AMS,广播中携带着Filter。

  2. AMS收到这个广播后,在receiver列表中,根据filter找到对应的receiver,可能是多个,把它们都放到一个广播队列中。最后向AMS的消息队列发送一个消息。

    当消息队列中的这个消息被处理时,AMS就从广播队列中找到合适的receiver,向广播接收者所在的进程发送广播。

  3. receiver所在的进程收到广播,并没有把广播直接发给receiver,而是将广播封装成一个消息,发送到主线程的消息队列中,当这个消息被处理时,才会把这个消息中的广播发送给receiver。

我们下面通过图,仔细看一下这3个阶段:

第1步,Service发送广播给AMS

在这里插入图片描述
发送广播,是通过Intent这个参数,携带了Filter,从而告诉AMS,什么样的receiver能接受这个广播。

第2步,AMS接收广播,发送广播。

收广播和发送广播是不同步的。AMS每接收到一个广播,就把它扔到广播发送队列中,至于发送是否成功,它就不管了。

因为receiver分为无序receiver和有序receiver,所以广播发送队列也分为两个,分别发送这两种广播。

AMS发送广播给客户端,这又是一个跨进程通信,还是通过ATP,把消息发给APT。因为要传递Receiver这个对象,所以它也是一个Binder对象,才可以传过去。我们前面说过,在把Receiver注册到AMS的时候,会把Receiver封装为一个IIntentReceiver接口的Binder对象。那么接下来,AMS就是把这个IIntentReceiver接口对象传回来。

第3步,App处理广播
在这里插入图片描述

这个流程描述如下:

  1. 消息从AMS传到客户端,把AMS中的IIntentReceiver接口对象转为InnerReceiver对象,这就是receiver,这是一个AIDL跨进程通信。

  2. 然后在ReceiverDispatcher中封装一个Args对象(这是一个Runnable对象,要实现run方法),包括广播接收者所需要的所有信息,交给ActivtyThread来发送

  3. 接下来要走的路就是我们所熟悉的了,ActivtyThread把Args消息扔到H这个Hanlder中,向主线程消息队列发送消息。等到执行Args消息的时候,自然是执行Args的run方法。

  4. 在Args的run方法中,实例化一个Receiver对象,调用它的onReceiver方法。

  5. 最后,在Args的run方法中,随着Receiver的onReceiver方法调用结束,会通过AMN/AMP发送一个消息给AMS,告诉AMS,广播发送成功了。AMS得到通知后,就发送广播给下一个Receiver。

注意:InnerReceiver是IIntentReceiver的stub,是Binder对象的接收端。

广播的种类

Android广播按发送方式分类有三种:无序广播、有序广播(OrderedBroadcast)和粘性广播(StickyBroadcast)。

  1. 无序广播是最普通的广播。

  2. 有序广播区别于无序广播,就在于它可以指定优先级。

这两种receiver存在AMS不同的变量中,可以认为是两个receiver集合,发送不同类别的广播。

  1. 粘性广播是无序广播的一种。

粘性广播,我们平常见的不多,但我说一个场景你就明白了,那就是电池电量。当电量小于20%的时候,就会提示用户。
而获取电池的电量信息,就是通过广播来实现的。
但是一般的广播,发完就完了。我们需要有这样一种广播,发出后,还能一直存在,未来的注册者也能收到这个广播,这种广播就是粘性广播。

由于动态receiver只能在Activity的onCreate()方法调用时才能注册再接收广播,所以当程序没有运行就不能接受到广播;但是静态注册的则不依赖于程序是否处于运行状态。

ContentProvider 工作原理

1.CP怎么用

我们快速回顾一下在App中怎么使用CP。
在这里插入图片描述

  1. 定义CP的App1
    在App1中定义一个CP的子类MyContentProvider,并在Manifest中声明,为此要在MyContentProvider中实现CP的增删改查四个方法:

在这里插入图片描述

在这里插入图片描述

  1. 使用CP的App2:
    在App2访问App1中定义的CP,为此,要使用到ContentResolver,它也提供了增删改查4个方法,用于访问App1中定义的CP:
    在这里插入图片描述

首先我们看一下ContentResolver的增删改查这4个方法的底层实现,其实都是和AMS通信,最终调用App1的CP的增删改查4个方法,后面我们会讲到这个流程是怎么样的。

其次,URI是CP的身份证,唯一标识。

我们在App1中为CP声明URI,也就是authorities的值为baobao,那么在App2中想使用它,就在ContentResolver的增删改查4个方法中指定URI,格式为:

uri = Uri.parse("content://baobao/");

接下来把两个App都进入debug模式,就可以从App2调试进入App1了,比如说,query操作。

2.CP的本质

各种数据源,有各种格式,比如短信、通信录,它们在SQLite中就是不同的数据表,但是对外界的使用者而言,就需要封装成统一的访问方式,比如说对于数据集合而言,必须要提供增删改查四个方法,于是我们在SQLite之上封装了一层,也就是CP。

3.匿名共享内存(ASM)

CP读取数据使用到了匿名共享内存,英文简称ASM,所以你看上面CP和AMS通信忙的不亦乐乎,其实下面别有一番风景。

关于ASM的概念,它其实也是个Binder通信,我画个图哦,你们就明白了:

在这里插入图片描述
这里的CursorWindow就是匿名共享内存。

这个流程,简单来说是这样的:

1)Client内部有一个CursorWindow对象,发送请求的时候,把这个CursorWindow类型的对象传过去,这个对象暂时为空。

2)Server收到请求,搜集数据,填充到这个CursorWindow对象。

3)Client读取内部的这个CursorWindow对象,获取到数据。

由此可见,这个CursorWindow对象,就是匿名共享内存,这是同一块匿名内存。

举个生活中的例子就是,你定牛奶,在你家门口放个箱子,送牛奶的人每天早上往这个箱子放一袋牛奶,你睡醒了去箱子里取牛奶。这个牛奶箱就是匿名共享内存。

4.CP与AMS的通信流程

还是拿App2想访问App1中定义的CP为例子。我们就看CP的insert方法。
在这里插入图片描述
上面这5行代码,包括了启动CP和执行CP方法两部分,分水岭在insert方法,insert方法的实现,前半部分仍然是在启动CP,当CP启动后获取到CP的代理对象,后半部分是通过代理对象,调用insert方法。

整体的流程如下图所示:

在这里插入图片描述
1)App2发送消息给AMS,想要访问App1中的CP。

2)AMS检查发现,App1中的CP没启动过,为此新开一个进程,启动App1,然后获取到App1启动的CP,把CP的代理对象返回给App2。

3)App2拿到CP的代理对象,也就是IContentProvider,就调用它的增删改查4个方法了,接下来就是使用ASM来传输数据或者修改数据了,也就是上面提到的CursorWindow这个类,取得数据或者操作结果即可,作为App的开发人员,不需要知道太多底层的详细信息,用不上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/469128.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

农业信息化有哪些SCI期刊推荐? - 易智编译EaseEditing

以下是一些农业信息化领域的SCI期刊推荐: Computers and Electronics in Agriculture: 该期刊涵盖了计算机和电子技术在农业生产和食品生产中的应用,如精准农业、农业机械自动化、智能传感器、农业模型和模拟等方面的研究。 影响因子为4.5…

离散数学组合计数

基本的组合计数公式 主要内容 加法法则和乘法法则排列与组合二项式定理与组合恒等式多项式定理 加法法则和乘法法则 加法法则乘法法则分类处理与分步处理 问题1:某旅游团从南京到上海,可以乘骑车,也可以乘火车,假定骑车每日有…

PHP源码的加密方法分享

关于PHP PHP是一种易于学习和使用的服务器端脚本语言。只需要很少的编程知识你就能使用PHP建立一个真正交互的WEB站点。 PHP是能让你生成动态网页的工具之一。PHP网页文件被当作一般HTML网页文件来处理并且在编辑时你可以用编辑HTML的常规方法编写PHP。 有些时候,我们需要对P…

流程图拖拽视觉编程-流程编辑器

目录 一、简介 二、流程编辑器-视图实现 三、参考资料 一、简介 前期文章: 流程图拖拽视觉编程--概述_Jason~shen的博客-CSDN博客 本期内容: 本期将介绍流程编辑器模块的实现方法,效果图如下所示。该模块基于QT Graphics/View实现&…

AVL 树(自平衡二叉搜索树) 介绍

AVL 树(自平衡二叉搜索树) 介绍 前言 在介绍二叉搜索树的章节中提到,二叉搜索树可能退化为线性链表,失去作为二叉树的各种优势。那么过程中需要维持二叉树的形式,同时左右子树的深度差异可控,如果能实现这两个条件&a…

要强的董经贵,与跳不出两轮电动车品质“陷阱”的雅迪

文|智能相对论 作者|陈明涛 “雅迪缺乏一个明确的战略定位,整个企业都没有方向,直接导致雅迪在前期竞争中失去先机。” “行业主要竞争对手更擅长销售和价格战,雅迪一直疲于应付、资源消耗大,结果也不理想。” “团队和经销商…

weblogic 反序列化 (CVE-2020-2551)漏洞复现(vulfocus)

漏洞简介 weblogic 反序列化 (CVE-2020-2551)漏洞是基于IIOP协议执行远程代码进行利用。 启动服务 http://192.168.5.128:10710/console/login/LoginForm.jsp 首先打开此连接,初始化weblogic服务。 准备工具 exp-自己写一个即可. javac…

企业编码生成系统--Python基础项目(4)

1. 成品展示🚗🚀🛫 运行🚀🚀🚀:1.在PyCharm中运行《企业编码生成系统》即可进入如图1所示的系统主界面。2.在该界面中可以选择要使用功能对应的菜单进行不同的操作。3.在选择功能菜单时&#x…

搭建electron-vue

electron-vue 准备工作修改package.jsonappveyor.yml.travis.yml.gitignore.eslintrc.js.eslintignore.babelrcsrc/renderer/main.jssrc/renderer/App.vuesrc/renderer/store/index.jssrc/renderer/store/modules/Counter.jssrc/renderer/store/modules/Counter.jssrc/renderer…

GT928 TP驱动跟读及虚拟按键上报解析

目前公司TP常用一套代码。MTK 平台使用.ko形式加载,所以跟读一下加深理解。 static struct i2c_driver tpd_i2c_driver {.driver {.of_match_table of_match_ptr(gt9xx_dt_match),},.probe tpd_i2c_probe,.remove tpd_i2c_remove,.detect tpd_i2c_detect,.dr…

万物皆数,算无止境 | 「雪浪算力开发者大赛」圆满收官

时在中春,阳和方起。 4月23日,「雪浪算力开发者大赛」 在雪浪小镇迎来完美收官。 本次大赛吸引了全球超过2422名开发者, 共有624支企业队伍, 288支高校队伍报名参赛, 累计提交作品9895份。 经过三个月激烈的角逐…

Linux shell编程 循环语句for continue break

for循环是编程语言中一种循环语句 示例1:循环读取user.txt中的用户名,创建用户。设置密码。 for i in $(cat /opt/user.txt) douseradd $iecho 123456 | passwd --stdin $i done 示例2:循环读取ipaddr文本文件中地址,执行ping命令…

基于空间矢量脉宽调制(SVPWM)的并网逆变器研究(Simulink)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

day38—选择题

文章目录 1.在计算机网络中,TCP和UDP协议的相似之处是(D)2.下列哪项最恰当地描述了建立TCP连接时“第一次握手”所做的工作(C)3.关于以下 URL 的描述错误的是(A)4.不属于交换机攻击的是&#xf…

LangChain入门指南

LangChain入门 什么是LangChain如何使用 LangChain?LangChain的模型LangChain 的主要特点使用示例构建语言模型应用程序:LLMPrompt Templates: 管理LLMs的Prompts构建语言模型应用程序:Chat Model完整代码 什么是LangChain LangChain是一个强…

【C语言】leetcode每日一题

目录 前言题目描述题目分析代码描述 前言 时间过得真快,马上又要回家了,马上又要开始卷了。不是每朵鲜花都能代表爱情,但是玫瑰做到了;不是每棵树都能耐得住干渴,但是白杨做到了;不是每个人都在追求上进&a…

【算法思维】-- 动态规划(C++)

OJ须知: 一般而言,OJ在1s内能接受的算法时间复杂度:10e8 ~ 10e9之间(中值5*10e8)。在竞赛中,一般认为计算机1秒能执行 5*10e8 次计算。 时间复杂度取值范围o(log2n)大的离谱O(n)10e8O(nlog(n))10e6O(nsqrt(…

从CI/CD持续集成部署到DevOps研发运维一体化

今天整理下从传统的CI/CD到DevOps研发运维一体化的整个演进过程。类似于每日构建和冒烟测试,实际上在10多年前就已经在实践,比如当前用的笔记多的AntCruiseControl方式来实现自动化的编译构建和持续集成能力。 包括当前DevOps过程实践中的持续集成&…

基于Springboot的班级综合测评管理系统的设计与实现

摘要 随着互联网技术的高速发展,人们生活的各方面都受到互联网技术的影响。现在人们可以通过互联网技术就能实现不出家门就可以通过网络进行系统管理,交易等,而且过程简单、快捷。同样的,在人们的工作生活中,也就需要…

Android内存泄漏问题排查分析及常见解决方案

什么是内存泄漏: 在Android开发过程中,当一个对象已经不需要再使用了,本该被回收时,而另个正在使用的对象持有它引用从而导致它不能被回收,这就导致本该被回收的对象不能被回收而停留在堆内存中,内存泄漏就…