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

news2024/11/15 6:00:17

前言

前面已经讲了什么是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 {
        // 创建Application
        app = data.info.makeApplication(data.restrictedBackupMode, null);
  ...
        if (!data.restrictedBackupMode) {
            if (!ArrayUtils.isEmpty(data.providers)) {
                // 安装ContentProvider
                installContentProviders(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 {
            // 创建ContentProvider
            final java.lang.ClassLoader cl = c.getClassLoader();
            LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
            ...
            localProvider = packageInfo.getAppFactory()
                    .instantiateProvider(cl, info.name);
            provider = localProvider.getIContentProvider();
            ...
   // 把context设置给ContentProvider
            localProvider.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内外程序的一个机制,限制程序访问系统资源的权限。

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

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

相关文章

Spring日志

1.日志的作用 定位和发现问题(主要)系统监控数据采集日志审计...... 2.日志的使用 2.1 ⽇志格式的说明 2.2 打印日志 Spring集成了日志框架,直接使用即可 步骤: 1.定义日志对象 2.使⽤⽇志对象打印⽇志 RestController public class LoggerController {private static Logger…

Ecovadis认证评估什么 Ecovadis认证有哪些注意事

Ecovadis认证是一个全球性的企业可持续性评估平台&#xff0c;它通过评估企业在环境、劳工与人权、公平商业实践、可持续采购等四个领域的表现&#xff0c;帮助企业识别潜在风险&#xff0c;提升ESG(环境、社会和公司治理)绩效&#xff0c;实现可持续发展 Ecovadis认证注意事项…

Linux-文件系统与日志分析

系列文章目录 提示&#xff1a;仅用于个人学习&#xff0c;进行查漏补缺使用。 1.Linux介绍、目录结构、文件基本属性、Shell 2.Linux常用命令 3.Linux文件管理 4.Linux 命令安装(rpm、install) 5.Linux账号管理 6.Linux文件/目录权限管理 7.Linux磁盘管理/文件系统 8.Linu…

MapReduce 简单介绍

MapReduce 一、MapReduce概述二、MapReduce 基本设计思想分而治之2.2 抽象成模型2.3 上升到框架 三、MapReduce 优缺点3.1 MapReduce 的优点3.1 MapReduce 的缺点 四、MapReduce 编程模型4.1 MapReduce 分布式计算原理4.2 MapReduce 编程模型4.3 剖析 MapReduce 编程模型4.3.1 …

好书推荐!《Building LLM Apps》构建大语言模型LLM应用!一次性讲清楚!

《Building LLM Apps》这本书是一份全面而实用的指南&#xff0c;它不仅介绍了大型语言模型&#xff08;LLM&#xff09;的基础知识和前沿技术&#xff0c;还深入探讨了如何将这些模型应用到实际的AI应用中。 书中从对LLM的深入介绍入手&#xff0c;接着探讨了包括GPT 3.5、GP…

RxJava在Android中的应用

RxJava是一个基于事件流、异步和响应式编程的库&#xff0c;它在Android开发中广泛用于简化异步操作和事件处理。通过RxJava&#xff0c;我们可以以声明式方式管理异步任务&#xff0c;并有效整合多个数据源。 1. RxJava核心组件介绍 1.1 Observable与Observer Observable&a…

大模型面试系列-大模型算法工程师的面试题目与解答技巧详细说明

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下大模型面试系列-大模型算法工程师的面试题目与解答技巧详细说明。 文章目录 大模型算法工程师面试题1. Llama 2 中使用的注意力机制是什么&#xff1f;描述一下查询分组注意力。2. LangChain 的结构详细描述一下。…

2024年8月15日嵌入式学习

今日主要学习线程和线程的互斥锁 pthread_cancel函数 它用于取消一个线程&#xff0c;当一个线程收到取消的申请时&#xff0c;他不会立即停止&#xff0c;而是在下一个取消点处结束运行&#xff0c;取消点是程序中一个特定的位置。如果线程在执行一个不可中断的系统调用&…

网络安全风险扫描原理及工具使用

课程目标 1.熟悉常见网络安全风险扫描工具 2.了解网络安全风险扫描原理 3.掌握扫描工具使用方法 为什么要做网络安全风险扫描&#xff1f; 什么是网络安全风险扫描&#xff1f; 通过一定的技术手段发现系统和软件存在的安全漏洞、弱口令 网络安全风险扫描的目的&#xff1…

【AI 绘画】web_ui 搭建(基于gradio)

AI 绘画- web_ui 搭建(基于gradio) 1. 内容介绍 Gradio的优势在于易用性,代码结构相比Streamlit简单,只需简单定义输入和输出接口即可快速构建简单的交互页面,更轻松部署模型。适合场景相对简单,想要快速部署应用的开发者。便于分享:gradio可以在启动应用时设置share=…

QT文件操作实战

QT文件操作实战 页面布局如下 读取文件:文件→界面文本框 采用“浏览”按钮的槽函数,编写的代码如下 void Widget::on_pushButton_clicked() {//读取txt文件,获取要打开的文件名,并将文件名(包含)填入lineEdit中// QString fileName = QFileDialog::getOpenFileName(th…

云HIS平台源码,云医院管理信息系统源码,云HIS医疗卫生管理系统源码

云医院管理信息系统源码&#xff0c;云HIS医疗卫生管理系统源码&#xff0c;医疗云HIS系统源码&#xff0c;自主版权二级医院应用案例 云HIS平台采用SaaS服务模式&#xff0c;软件使用者无需购置额外硬件设备、软件许可证及安装和维护软件系统&#xff0c;通过互联网浏览器在任…

YS9082HP量产工具,支持N38B开卡(ID:89D3AC32C204),解决YS9082HP N38B开卡到87%报错,状态8817,Fail:写表失败

收的固态硬盘&#xff0c;主控是YS9082HP&#xff0c;颗粒是Intel的N38B&#xff1a; 从网上找了个YS9082HP_MPToolV8.00.00.01.025_HPS2704M_release_N38B版本试试&#xff0c;倒是能识别颗粒&#xff0c;到87%就报错&#xff0c;Fail:写表失败&#xff0c;错误状态是8817&…

山东易注册网络科技有限公司:合伙人模式的机遇与创新

在互联网高速发展的今天&#xff0c;合伙人模式成为网络运营的新趋势。山东易注册网络科技有限公司以其创新的合伙人模式&#xff0c;为用户带来了前所未有的机遇。 加入山东易注册的合伙人&#xff0c;可以享受到独立搭建系统和独立服务器的权益。用户可以打造自己的独立域名和…

怎样用python函数画图像

打开Python的shell界面&#xff0c;如图所示。&#xff08;注意我们需要已经安装了matplotlib库包&#xff09;。 输入以下代码&#xff0c;导入我们用到的函数库。 >>> import numpy as np >>> import matplotlib.pyplot as plt 产生我们要画的的函数的数据…

数据集的简单制作和使用

数据集的简单制作和使用 参考资料&#xff1a;Labelme分割标注软件使用 使用labelme软件对数据集进行分割 每张图片获得一个json文件 我们看看其中一个文件&#xff0c;内容包含每个点在图片中的位置 我们可以自己写一个脚本&#xff08;或使用别人的&#xff09;将上述json…

突破传统看车局限,3DCAT实时云渲染为东风日产奇骏赋能

在当今数字化飞速发展的时代&#xff0c;汽车行业的营销也面临着诸多变革与挑战。线下展示由于受到场地空间的限制&#xff0c;往往无法全面展示所有车型&#xff0c;且建设成本高昂。而一些销售门店可能因位置偏僻等因素&#xff0c;导致客户上门看车、试驾的邀约变得困难重重…

哈工大李治军老师OS课程笔记(4)——内存管理

一 内存使用与分段&#xff08;实验六&#xff09; 内存是如何用起来的&#xff1f; 内存使用&#xff1a;将程序放在内存中&#xff0c;PC指向开始地址 重定位&#xff1a;修改程序中的地址&#xff08;是相对地址&#xff09; 什么时候完成重定位&#xff1f; 编译时加基址…

航空航天之归零报告

归零报告 1.技术归零报告1.1技术归零报告模板1.2撰写技术归零报告注意事项 2.管理归零报告2.1管理归零报告模板2.2撰写管理归零报告注意事项 归零报告通常指的是将项目、任务或系统的状态重置为初始状态&#xff0c;进行数据清零、状态复位或问题总结的报告文档。 在航空航天、…

spring揭秘02-springbean生命周期(实例化过程)

文章目录 【README】【1】spring构建应用系统分2个阶段【1.1】spring容器启动阶段【1.2】springbean实例化阶段 【2】springbean生命周期概述【3】springbean生命周期过程【3.1】**第1步-实例化bean对象**【3.2】**第2步-设置对象属性**【3.3】 第3步-检查Aware接口并设置相关依…