当前位置: 首页 > news >正文

Android全面解析之context机制(三): 从源码角度分析context创建流程(下)

前言

前面已经讲了什么是context以及从源码角度分析context创建流程(上)。限于篇幅把四大组件中的广播和内容提供器的context获取流程放在了这篇文章。广播和内容提供器并不是context家族里的一员,所以他们本身并不是context,因而他们的context肯定是直接或间接从Application、Activity或者Service获取。然后对context的设计进行了讨论,从更高的角度看context,能够帮助我们看到context的本质,也能帮助我们更好地理解并使用context。

Broadcast的context获取流程

Broadcast和上面的组件不同,他并不是继承自Context,所以他的Context是需要通过Application、Activity或者Service来给予。我们一般使用广播的context是在接收器中,如:

class MyClass :BroadcastReceiver() {override fun onReceive(context: Context?, intent: Intent?) {TODO("use context")}
}

那么onReceive的context对象是从哪里来的呢?同样我们先看广播接收器的注册流程:

同样,详细的广播相关工作流程可以阅读Android广播Broadcast的注册与广播源码过程详解(基于api29)这篇文章了解。因为在创建Receiver的时候并没有传入context,所以我们需要追踪他的注册流程,看看在哪里获取了context。我们先看到ContextImpl的registerReceiver方法:

ContextImpl.class(api29)
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,String broadcastPermission, Handler scheduler) {// 注意参数return registerReceiverInternal(receiver, getUserId(),filter, broadcastPermission, scheduler, getOuterContext(), 0);
}

registerReceiver方法最终会来到这个重载方法,我们可以注意到,这里有个getOuterContext,这个是什么?还记得Activity的context创建过程吗?这个方法获取的就是activity本身。我们继续看下去:

ContextImpl.class(api29)
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) {...rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,mMainThread.getInstrumentation(), true);}...}...
}

这里利用context创建了ReceiverDispatcher,我们继续深入看:

LoadedApk.class(api29)
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,Context context, Handler handler,Instrumentation instrumentation, boolean registered) {synchronized (mReceivers) {LoadedApk.ReceiverDispatcher rd = null;...if (rd == null) {rd = new ReceiverDispatcher(r, context, handler,instrumentation, registered);...}...}
}ReceiverDispatcher.class(api29)
ReceiverDispatcher(..., Context context,...) {...mContext = context;...
}

这里确实把receiver和context创建了ReceiverDispatcher,嗯?怎么没有给Receiver?其实这涉及到广播的内部设计结构。Receiver是没有跨进程通信能力的,而广播需要AMS的调控,所以必须有一个可以跟AMS沟通的对象,这个对象是InnerReceiver,而ReceiverDispatcher就是负责维护他们两个的联系,如下图:

而onReceive方法也是由ReceiverDispatcher回调的,最后我们再看到回调onReceive的那部分代码:

ReceiverDispatcher.java/Args.class;
public final Runnable getRunnable() {return () -> {...;try {...;// 可以看到这里回调了receiver的方法,这样整个接收广播的流程就走完了。receiver.onReceive(mContext, intent);}}
}

Args是Receiver的内部类,mContext就是在创建ReceiverDispatcher时传入的对象,到这里我们就知道这个对象确实是Activity了。

但是,,不一定每个都是Activity。在源码中我们知道是通过getOuterContext来获取context,如果是通过别的context注册广播,那么对应的对象也就不同了,只是我们一般都是在Activity中创建广播,所以这个context一般是activity对象。

ContentProvider的context获取流程

ContextProvider我们用的就比较少了,内容提供器主要是用于应用间内容共享的。虽然ContentProvider是由系统创建的,但是他本身并不属于Context家族体系内,所以他的context也是从其他获取的。老样子,先看ContentProvider的创建流程:

咦?这不是Application创建的流程图吗?是的,ContentProvider是伴随着应用启动被创建的,来看一张更加详细的流程图:

我们把目光聚集到ContentProvider的创建上,也就是installContentProviders方法。同样,详细的ContentProvider工作流程可以访问Android中ContentProvider的启动与请求源码流程详解(基于api29)这篇文章。installContentProviders是在handleBindApplication中被调用的,我们看到调用这个方法的地方:

private void handleBindApplication(AppBindData data) {try {// 创建Applicationapp = data.info.makeApplication(data.restrictedBackupMode, null);...if (!data.restrictedBackupMode) {if (!ArrayUtils.isEmpty(data.providers)) {// 安装ContentProviderinstallContentProviders(app, data.providers);}}    
}

可以看到这里传入了application对象,我们继续看下去:

private void installContentProviders(Context context, List<ProviderInfo> providers) {final ArrayList<ContentProviderHolder> results = new ArrayList<>();for (ProviderInfo cpi : providers) {...ContentProviderHolder cph = installProvider(context, null, cpi,false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);...}
...
}

这里调用了installProvider,继续往下看:

private ContentProviderHolder installProvider(Context context,ContentProviderHolder holder, ProviderInfo info,boolean noisy, boolean noReleaseNeeded, boolean stable) {ContentProvider localProvider = null;IContentProvider provider;if (holder == null || holder.provider == null) {...// 这里c最终是由context构造的Context c = null;ApplicationInfo ai = info.applicationInfo;if (context.getPackageName().equals(ai.packageName)) {c = context;}...try {// 创建ContentProviderfinal java.lang.ClassLoader cl = c.getClassLoader();LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);...localProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);provider = localProvider.getIContentProvider();...// 把context设置给ContentProviderlocalProvider.attachInfo(c, info);} ...} ...
}

这里最重要的一行代码是localProvider.attachInfo(c, info);,在这里把context设置给了ContentProvider,我们再深入一点看看:

ContentProvider.class(api29)
public void attachInfo(Context context, ProviderInfo info) {attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {...if (mContext == null) {mContext = context;...}...
}

这里确实把context赋值给了ContentProvider的内部变量mContext,这样ContentProvider就可以使用Context了。而这个context正是一开始传进来的Application。

总结

Context承受的两大重要职责是:身份权限、程序访问系统的接口。一个Java类,如果没有context那么就是一个普通的Java类,而当他获得context那么他就可以称之为一个组件了,因为它获得了访问系统的权限,他不再是一个普通的身份,是属于android“公民”了。而“公民”并不是无法无天,系统也可以通过context来封装以及限制程序的权限。要想弹出一个通知,你必须通过这个api,用户关闭你的通知权限,你就别想通过第二条路来弹出通知了。同时 程序也无需知道底层到底是如何实现,只管调用api即可。四大组件为何称为四大组件,因为他们生来就有了context,特别是activity和service,包括Application。而我们写的一切程序,都必须间接或者直接从其中获取context。

总而言之,context就是负责区分android内外程序的一个机制,限制程序访问系统资源的权限。

相关文章:

Android全面解析之context机制(三): 从源码角度分析context创建流程(下)

前言 前面已经讲了什么是context以及从源码角度分析context创建流程&#xff08;上&#xff09;。限于篇幅把四大组件中的广播和内容提供器的context获取流程放在了这篇文章。广播和内容提供器并不是context家族里的一员&#xff0c;所以他们本身并不是context&#xff0c;因而…...

执行docker compose命令出现 Additional property include is not allowed

问题背景 在由docker-compose.yml的文件目录下执行命令 docker compose up -d 出现错误 Additional ininoperty include is not allowed 原因 我的docker-compose.yml 文件中出现了include标签旧版本的docker-compose 不支持此标签 解决办法 下载支持的docker-compose 解决…...

STM32通过I2C硬件读写MPU6050

目录 STM32通过I2C硬件读写MPU6050 1. STM32的I2C外设简介 2. STM32的I2C基本框图 3. STIM32硬件I2C主机发送流程 10位地址与7位地址的区别 7位主机发送的时序流程 7位主机接收的时序流程 4. STM32硬件与软件的波形对比 5. STM32配置硬件I2C外设流程 6. STM32的I2C.h…...

ubuntu2204-中文输入法-pycharm-python-django开发环境搭建

文章目录 1.系统常用设置1.1.安装中文输入法1.2.配置输入法1.3.卸载输入法1.4.配置镜像源2.java安装3.pycharm安装与启动4.卸载ubuntu2204默认版本5.安装Anaconda5.1.安装软件依赖包5.2.安装命令5.3.激活安装5.4.常用命令5.5.修改默认启动源6.安装mysql6.1.离线安装mysql6.2.在…...

【学习笔记】Matlab和python双语言的学习(一元线性回归)

文章目录 前言一、一元线性回归回归分析的一般步骤一元线性回归的基本形式回归方程参数的最小二乘法估计对回归方程的各种检验估计标准误差的计算回归直线的拟合优度判定系数显著性检验 二、示例三、代码实现----Matlab四、代码实现----python回归系数的置信区间公式残差的置信…...

LeetCode //C - 316. Remove Duplicate Letters

316. Remove Duplicate Letters Given a string s, remove duplicate letters so that every letter appears once and only once. You must make sure your result is the smallest in lexicographical order among all possible results. Example 1: Input: s “bcabc”…...

【ARM+Codesys 客户案例 】RK3568/A40i/STM32+CODESYS在工厂自动化中的应用:PCB板焊接机

现代化生产中&#xff0c;电子元件通常会使用自动化设备来进行生产&#xff0c;例如像PCB&#xff08;印刷电路板&#xff09;的组装。但是生产过程中也会面临一些问题&#xff0c;类似于如何解决在PCB板上牢固、精准地安装各种组件呢&#xff1f;IBL Lttechnik GmbH公司的CM80…...

【二分查找】--- 初阶题目赏析

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 算法Joureny 上篇我们讲解了关于二分的朴素模板和边界模板&#xff0c;本篇博客我们试着运用这些模板。 &#x1f3e0; 搜索插入位置 &#x1f4cc; 题目…...

【PostgreSQL003】PostgreSQL数据表空间膨胀,磁盘爆满,应用宕机(经验总结,已更新)

1.一直以来想写下基于PostgreSQL的系列文章&#xff0c;作为较火的数据ETL工具&#xff0c;也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下PostgreSQL数据库相关知识体系。空间膨胀&#xff08;主键、外键、…...

C语言第20天笔记

文件操作 概述 什么是 文件 文件时保存在外存储器上&#xff08;一般代指磁盘&#xff0c;也可以是U盘、移动硬盘等&#xff09;的数据的集合。 文件操作体现在哪几个方面 1. 文件内容的读取 2. 文件内容的写入 数据的读取和写入可被视为针对文件进行输入和输出的操作&a…...

为什么穷大方

为什么有些人明明很穷&#xff0c;却非常的大方呢&#xff1f; 因为他们认知太低&#xff0c;根本不懂钱的重要性&#xff0c;总是想着及时享乐&#xff0c;所以一年到头也存不了什么钱。等到家人孩子需要用钱的时候&#xff0c;什么也拿不出来&#xff0c;还到处去求人。 而真…...

HiveSQL实战——大数据开发面试高频SQL题

查询每个区域的男女用户数 0 问题描述 每个区域内男生、女生分别有多少个 1 数据准备 use wxthive; create table t1_stu_table (id int,name string,class string,sex string ); insert overwrite table t1_stu_table values(4,张文华,二区,男),(3,李思雨,一区,女),(1…...

RabbitMQ集群 - 普通集群搭建、宕机情况

文章目录 RabbitMQ 普通集群概述集群搭建数据准备启动容器宕机情况 RabbitMQ 普通集群 概述 1&#xff09;普通模式中所有节点没有主从之分&#xff0c;所有节点的元数据&#xff08;交换机、队列、绑定等&#xff09;都是一致的. 例如只要有任意一个节点上面 新增交换机&…...

xssDOM型练习

文章目录 例1要求 例2代码解析方法 例3例4例5例6例7例8 例1 本题通过get接收并传递参数&#xff0c;所有参数不经过过滤直接放入h2标签里面。 要求 1.需要页面弹出1337 2.不能与用户交互 官方认为innerHTML中script标签不安全&#xff0c;所以将其禁用&#xff0c;但只禁用了…...

python中的gradio使用麦克风时报错

python中的gradio使用麦克风时报错 当运行至 import gradio as gr with gr.Blocks() as demo:with gr.Tab("microphone transcriber"):gr.Audio(source"microphone", type"numpy", streamingTrue)demo.queue()##访问链接 https://ip:1235/demo…...

Oracle(63)什么是临时表(Temporary Table)?

临时表&#xff08;Temporary Table&#xff09;是一种特殊类型的表&#xff0c;用于存储临时数据&#xff0c;这些数据在会话期间或事务期间是短暂的。临时表在不同的数据库系统中都有实现&#xff0c;但功能和特性可能有所不同。临时表通常用于存储中间计算结果、临时数据处理…...

《Techporters架构搭建》-Day06 国际化

什么是国际化&#xff1f; 国际化&#xff0c;也叫i18n&#xff0c;为什么叫i18n呢&#xff1f; "i18n"是国际化&#xff08;internationalization&#xff09;的缩写&#xff0c;数字18代表了国际化这个单词中间的字母数量。类似这样的缩写还有k8s&#xff08;kube…...

Linux ACL 访问控制

今天给伙伴们分享一下Linux ACL 访问控制&#xff0c;希望看了有所收获。 我是公众号「想吃西红柿」「云原生运维实战派」作者&#xff0c;对云原生运维感兴趣&#xff0c;也保持时刻学习&#xff0c;后续会分享工作中用到的运维技术&#xff0c;在运维的路上得到支持和共同进步…...

hg transformers pipeline使用

什么是hg transformers pipeline? 在Hugging Face的transformers库中&#xff0c;pipeline是一个高级API&#xff0c;它提供了一种简便的方式来使用预训练模型进行各种NLP任务&#xff0c;比如情感分析、文本生成、翻译、问答等。通过pipeline&#xff0c;你可以在几行代码内…...

高性能内存对象缓存

Memcached概述 一套开源的高性能分布式内存对象缓存系统 所有的数据都存储在内存中 支持任意存储类型的数据 提高网站的访问速度 数据存储方式与数据过期方式 数据存储方式:Slab Allocation 按组分配内存&#xff0c;每次先分配一个Slab&#xff0c;相当于一个大小为1M的页&…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

莫兰迪高级灰总结计划简约商务通用PPT模版

莫兰迪高级灰总结计划简约商务通用PPT模版&#xff0c;莫兰迪调色板清新简约工作汇报PPT模版&#xff0c;莫兰迪时尚风极简设计PPT模版&#xff0c;大学生毕业论文答辩PPT模版&#xff0c;莫兰迪配色总结计划简约商务通用PPT模版&#xff0c;莫兰迪商务汇报PPT模版&#xff0c;…...

Linux部署私有文件管理系统MinIO

最近需要用到一个文件管理服务&#xff0c;但是又不想花钱&#xff0c;所以就想着自己搭建一个&#xff0c;刚好我们用的一个开源框架已经集成了MinIO&#xff0c;所以就选了这个 我这边对文件服务性能要求不是太高&#xff0c;单机版就可以 安装非常简单&#xff0c;几个命令就…...

算术操作符与类型转换:从基础到精通

目录 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符&#xff1a;、-、*、/、% 赋值操作符&#xff1a;和复合赋值 单⽬操作符&#xff1a;、--、、- 前言&#xff1a;从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...