当前位置: 首页 > 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的页&…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...