理解Android上下文Context

2023-11-12

Context使用场景总的来说分为两大类:

  • 使用Context调用方法,比如启动Activity、访问资源、调用系统级服务等。
  • 调用方法时传入Context,比如弹出Toast、创建Dialog等。

  Activity、Service和Application都间接的继承自Context,一个应用进程中有多少个Context,这个数量等于Activity和Service的总个数加1,1指的是Application。
Context是一个抽象类,它的内部定义了很多方法及静态常量,具体实现类为ContextImpl。和Context相关联的类除了ContextImpl还有ContextWrapper,ContextThemeWrapper和Activity等。
  ContextImpl和ContextWrapper继承自Context,ContextWrapper内部包含Context类型的mBase对象,mBase具体指向ContextImpl。ContextImpl提供了很大功能,但是外界需要使用并拓展ContextImpl的功能,因此设计上使用了装饰模式,ContextWrapper是装饰类,它对ContextImpl进行包装,ContextWrapper主要是起了方法传递的作用,ContextWrapper中几乎所有的方法都是调用ContextImpl的相应方法来实现的。
  ContextThemeWrapper、Service、Application都继承自ContextWrapper,它们都可以通过mBase来使用Context的方法,同时它们也是装饰类,在ContextWrapper的基础上又添加了不同的功能。
  ContextThemeWrapper中包含和主题相关的方法,Activity继承于它。
Context的关联类采用了装饰模式,主要优点:

  • 使用者(比如Service)能够更方便地使用Context
  • 如果ContextImpl发生了变化,它的装饰类ContextWrapper不需要做任何修改
  • ContextImpl的实现不会暴露给使用者,使用者也不必关心ContextImpl的实现
  • 通过组合而非继承的方式拓展ContextImpl的功能,在运行时选择不同的装饰类,实现不同的功能
Application Context的创建过程

Application Context的创建时序图
  ActivityThread类作为应用程序进程的主线程管理类,它会调用内部类ApplicaitonThread的scheduleLaunchActivity方法来启动Activiy,在ApplicationThread的scheduleLaunchActivity方法中向H类发送LAUNCH_ACTIVITY类型的消息,在H类的handleMessage方法中对此消息进行处理,通过getPackageInfoNoCheck方法获得LoadedApk类型的对象,并将其赋值给ActivityClientRecord的成员变量packageInfo,然后调用ActivityThread的handleLaunchActivity方法,此方法又调用了performLaunchActivity方法,在此方法中有很多重要逻辑,这里只分析和Application Context相关的逻辑。
  在performLaunchActivity方法中,会调用r.packageInfo.makeApplication方法,packaeInfo时LoadedApk类型的,在LoadedApk的makeApplication方法中,先判断如果mApplication不为null则直接返回mApplication,如果为null,则通过ContextImpl的createContext方法来创建ContextImpl;在Instrumentation的newApplication方法中传入了ClassLoader类型的对象以及创建的ContextImpl对象,创建了Application,将Application赋值给ContextImpl的成员变量mOuterContext,这样ContextImpl中也包含了Application的引用。将Application赋值给LoadedApk的成员变量mApplication,这里mApplication是Application类型的,用来代表ApplicationContext。Instrumentation的newApplication方法中,通过反射来创建Application,并调用其attach方法,将ContextImpl传进去。在attach方法中调用了attachBaseContext方法,它在Application父类ContextWrapper中实现,将传过来的ContextImpl类型的base赋值给ContextWrapper的成员变量mBase,这样ContextWrapper中就可以使用Context的方法,而Application继承自ContextWrapper,同样可以使用Context的方法。Application的attach方法的作用就是使Application可以使用Context方法,这样Application才可以用来代表ApplicationContext。

Application Context的获取过程

  通过getApplicationContext方法来获取Application Context,此方法在ContextWrapper中实现,会调用ContextImpl中的getApplicationContext方法,在此方法中,如果LoadedApk类型的mPackageInfo不为null,则调用LoadedApk的getApplication方法,否则调用ActivityThread的getApplication方法,由于应用程序已启动,LoadedApk不会为null,因此会调用LoadedApk的getApplication方法,返回mApplication对象。

Activity的Context创建过程

Activity的Context创建时序图
  ActivityThread是应用程序进程的主线程管理类,它的内部类ApplicationThread会调用scheduleLaunchActivity方法来启动Activity,此方法将启动Activity的参数封装成ActivityClientRecord,sendMessage方法将向H类发送LAUNCH_ACTIVITY的消息,并将ActivityClientRecord传递过去。H类的handleMessage方法会对LAUNCH_ACTIVITY消息进行处理,其中调用了ActivityThread的handleLaunchActivity方法,而此方法又调用了performLaunchActivity方法。在performLaunchActivity中,通过createBaseContextForActivity方法来创建Activity的ContextImpl,通过Instrumentation的newActivity方法创建Activity实例,然后调用ContextImpl的setOuterContext方法,将Activity实例赋值给ContextImpl的成员变量mOuterContext,然后将ContextImpl实例传入Activity的attach方法中,这样ContextImpl也可以调用Activity的变量和方法。最后调用mInstrumentation的callActivityOnCreate方法调用Activity的onCreate方法。在createBaseContextForActivity方法中会调用ContextImpl的createActivityContext方法来创建ContextImpl。
  再看Activity的attach方法,此方法中会创建PhoneWindow,代表应用程序窗口,PhoneWindow在运行中间会接触很多事件,比如点击菜单弹出,屏幕焦点变化等事件,这些事件需要转发给关联的Activity,转发操作通过Window.Callback接口实现,Activity实现了这个接口,将当前Activity通过Window的setCallback方法传递给PhoneWindow,为PhoneWindow设置WindwoManager,并将WindowManager赋值给Activity的成员变量mWindowManager,这样Activity就可以通过getWindowManager方法来获取WindowManager。
  开始的attachBaseContext方法在ContextThemeWrapper中实现,直接调用了父类ContextWrapper的attachBaseContext方法,将一路传递过来的ContextImpl类型的base赋值给ContextWrapper的成员变量mBase,这样ContextWrapper的功能就可以交由ContextImpl来处理。
  在Activity的启动过程中创建ContextImpl,并赋值给ContextWrapper的成员变量mBase,Activity继承自ContextWrapper的子类ContextThemeWrapper,这样在Activity中就可以使用Context中定义的方法了。

Service的Context创建过程

  Service的Context创建过程与Activity的Context创建过程类似,是在Service的启动过程中被创建的。ActivityThread的内部类ApplicationThread会调用scheduleCreateService方法来启动Service,sendMessage方法向H类发送CREATE_SERVICE消息,H类的handleMessage方法会处理此消息,调用了ActivityThread的handleCreateService方法,通过ContextImpl的createApplicationContext方法创建了ContextImpl,并将其传入Service的attach方法中,attach方法中会调用ContextWrapper的attachBaseContext方法,将传递过来的ContextImpl类型的base赋值给ContextWrapper的成员变量mBase,这样ContextWrapper中就可以使用Context的方法,而Service继承自ContextWrapper同样可以使用Context的方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

理解Android上下文Context 的相关文章

  • 在 Android 应用程序中动态检查权限

    继我之前的question https stackoverflow com questions 18420994 multiple permissions in androidpermisson关于运行时的 android 权限强制执行 我
  • 如何将本地主机 IP 地址转发到 Android 模拟器?

    我知道可以将端口从我的开发机器转发到 Android 模拟器 但是这是如何完成的呢 我在 android developers 网站上找到了解决方案 但我不明白他们的说明是什么意思 有人对此有明确的指示吗 我的开发机器运行的是 Window
  • 如何更改Android应用程序的页面?

    我是安卓新手 我已经设计了由许多页面组成的应用程序 任何人都有源代码或任何可以让我知道如何构建它的东西 例如 当我单击按钮时 我希望它将页面更改为下一页 谢谢你卡 运行通过第一个应用程序教程 https developer android
  • ANDROID:Webview 和 httpclient 之间共享会话

    我的 WebView 中实际上有一个记录的会话 但我也使用 httpclient 从网络发送和获取数据 我在互联网上看到不可能获取WebView的内容 所以我需要使用我的httpclient从Web服务获取数据 问题是这个Web服务使用会话
  • 如何在运行时更改android应用程序名称和图标?

    安装 Android 应用程序后 当您按下应用程序中的按钮时 是否可以动态更改应用程序图标和名称 在运行时 这是到目前为止的代码 getPackageManager setComponentEnabledSetting new Compon
  • 空 Activity 中的内存泄漏

    我最近决定使用泄漏金丝雀 https github com square leakcanary在我的项目中 所以我创建了一个空的项目Activity只是为了测试 当我运行应用程序时 在没有逻辑代码或视图的项目创建之后 我从这个库中获得了内存
  • 尝试运行我的 Espresso 测试时 RecyclerViewMatcher 中出现 NullPointerException

    我想跑Espresso Testing on my Android project 第 1 步 找到我的RecyclerView 第 2 步 检查上的项目RecyclerView 第一步成功运行 但第二步应该检查recycler view项
  • android - 定期轮询服务器并将响应显示为通知[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在构建一个Android应用程序 我需要每三个小时从服务器获取一些通知数据 文本 并使用NotificationManager将其
  • 如何将点击事件从片段传递到容器活动?

    我正在实现一个导航抽屉活动 其中导航抽屉中有一个片段 由回收器视图组成 可以动态地将项目添加到导航抽屉 我已经成功实现了这个概念 但面临一个小问题 即当我单击片段中的任何项目时 导航抽屉不会关闭 这是因为我无法从片段访问 DrawerLay
  • 如何检测 Android 设备是否与 Android Wear 手表配对

    我正在创建一个 Android Wear 应用程序来扩展推送通知 当收到推送通知时 我的应用程序会从服务器下载大约 10 张图像 并在手表上显示这些附加图像 这些图像特定于 Android Wear 应用程序 不会显示在手持设备上 如何判断
  • 如何过滤EditText的输入?

    我想过滤一个的输入EditText 只允许使用数字和字母 首先我使用TextWatcher处理最后一个输入字符 但是当您移动光标或将某些内容粘贴到EditText 这个方法失败了 现在我想知道有没有办法过滤非法输入并反馈给用户 Add In
  • 如何从 Android 应用程序调试共享库 [单独的项目]

    我正在开发一个 Android 应用程序并在项目中使用密集的 C 代码 首先 我将c 源代码放入项目中 并使用以下指南在Android应用程序中具有本机调试的NDK功能 http tools android com recent using
  • Lcom/google/firebase/FirebaseApp 类中没有虚拟方法 zzbqo()Z;或其超类(“com.google.firebase.FirebaseApp”的声明

    在我的 Android 应用程序中 编译应用程序时出现错误 我正在最新的 android studio 中工作 并使用 Firebase UI Auth 和 Firebase 数据库 所有版本在应用程序级等级文件中都相同 那么为什么我收到此
  • Phonegap - navigator.app.backHistory() 不适用于 HTML 后退按钮

    在我的应用程序中 我使用phonegap 2 6 对于后退按钮 我使用以下函数 document addEventListener backbutton onBackKeyDown false function onBackKeyDown
  • 多少次函数调用会导致堆栈溢出

    你好 Android Java 开发者 当一个函数调用一个函数并且该函数调用另一个函数等等时 有多少次调用 堆栈长度 会让我陷入堆栈溢出 有一般经验法则吗 我问的原因是因为我现在对于我的 5 人纸牌游戏来说哪个更有效 设计明智 解决方案一
  • 注意通知持续时间

    是否可以将抬头通知的持续时间设置为无限 现在它只显示 5 秒 已经尝试过不同的事情 例如更改类别 但持续时间始终为 5 秒 这是我的代码 Notification notification notificationBuilder setCa
  • 如何添加不确定的进度条

    我的应用程序 UI 是使用 Android 支持库构建的 但目前没有我的应用程序真正需要的 不确定的 进度条的 AppCompat 版本 我宁愿不使用任何第三方库来实现材料设计进度条 所以我想知道是否有人坐在有关为什么它不包含在支持库中的信
  • Android:使用 ObjectAnimator 平移具有视图尺寸小数值的视图

    看来旧的视图动画 translate scale等 不再被接受AnimationInflater 至少截至 ICS 而言 我在 4 0 4 中阅读了它的代码 它明确只需要 XML 元素set objectAnimator animator
  • Android - 下载 JSON 数据并保存到共享首选项

    我正在从 PHP 服务读取 JSON 数据 每当该 JSON 的版本发生变化时 我想将其存储在 Android 上 用新数据替换旧数据 JSON 仅用于填充 Spinner 我的问题是 JSON 有 36KB 可以将其存储在共享首选项中有一
  • 如果 windowTranslucentStatus 为 false,则不会调用键盘的 onApplyWindowInsets

    正如标题所说 我有一个Activity我想在其上处理键盘的插入 底部有一个视图 不应该推上去 但其余的观点应该被推高 我可以使用很好地处理插图onApplyWindowInsets IF windowTranslucentStatus设置为

随机推荐

  • poco源码简单分析

    自动化工具poco源码简单分析 Airtest简介 Airtest是网易游戏开源的一款UI自动化测试项目 目前处于公开测试阶段 该项目分为AirtestIDE Airtest Poco Testlab四个部分 基于python脚本的方式 用
  • 【公告】博客专家 6 月发布原创/翻译文章奖励

    博客专家6月发布原创 翻译文章奖励 CSDN ID 所获奖励 malefactor 图灵社区技术图书 程序员杂志最新期刊 C币100 lmj623565791 图灵社区技术图书 程序员杂志最新期刊 C币100 jiangwei0910410
  • python TypeError: missing 1 required positional argument:'self'

    Python 调用类的函数时报错如下 TypeError seperate data missing 1 required positional argument self 报错原因 train data test data DataCle
  • 对spark dataframe join之后的列值NULL值进行填充为指定数值的操作

    众所周知 两个数据集如A B取JOIN操作的时候 其结果往往会出现NULL值的出现 这种情况是非常不利于后续的分析与计算的 特别是当涉及到对这个数值列进行各种聚合函数计算的时候 针对这种问题 当然从最简单的dataframe map来处理是
  • QThreadPool线程池的原理与使用

    一 为什么需要用线程池 现在所有的高性能服务器程序 几乎都会使用到线程池技术 从而更好且有效的榨干服务器性能 1 开多少个线程可以达到性能最佳 不知道 你有没有这个疑问 这是一种常见的线程使用方式 class MyThread public
  • list集合(接口)

    list集合 显而易见是用来存储数据的 可以把它看作是长度可变的数组 它是有序存储数据的 具有跟数组一样的索引 ArrayList LinkedList Vector Stack都是list接口的实现类 以ArrayList为例说明list
  • 1033 旧键盘打字 (20 分)

    题目 旧键盘上坏了几个键 于是在敲一段文字的时候 对应的字符就不会出现 现在给出应该输入的一段文字 以及坏掉的那些键 打出的结果文字会是怎样 输入格式 输入在 2 行中分别给出坏掉的那些键 以及应该输入的文字 其中对应英文字母的坏键以大写给
  • 基于Redis的BitMap实现签到、连续签到统计(含源码)

    微信公众号访问地址 基于Redis的BitMap实现签到 连续签到统计 含源码 推荐文章 1 springBoot对接kafka 批量 并发 异步获取消息 并动态 批量插入库表 2 SpringBoot用线程池ThreadPoolTaskE
  • mysql日期转换

    1 MySQL中和日期相关的函数 1 1 DATE FORMAT date format 主要用来将日期格式化函数 举例 SELECT DATE FORMAT NOW Y m d 1 2 STR TO DATE str format 主要用
  • 分析pandas的数据,分析某一列数据的长度分布等等

    分析数据 如分析sku的长度 import pandas as pd import numpy as np data file data data zh sku 80k csv 待分析的文件 def ana len file key Non
  • AI绘画:StableDiffusion实操教程-斗破苍穹-云韵-常服(附高清图下载)

    前段时间我分享了StableDiffusion的非常完整的教程 AI绘画 Stable Diffusion 终极宝典 从入门到精通 不久前 我与大家分享了StableDiffusion的全面教程 AI绘画 Stable Diffusion
  • HITICS-2018大作业 hello的一生

    摘 要 本论文详细介绍了hello程序在linux系统中从生成源代码到成功运行完毕被系统回收的整个过程 按照执行的先后顺序模块化介绍了hello c在计算机内部是系统具体执行了什么指令 如何执行的 用到了哪些知识等 本论文参考CSAPP课本
  • linux(centos) 保存退出vi编辑

    保存命令 按ESC键 跳到命令模式 然后 w 保存文件但不退出vi w file 将修改另外保存到file中 不退出vi w 强制保存 不推出vi wq 保存文件并退出vi wq 强制保存文件 并退出vi q 不保存文件 退出vi q 不保
  • Oracle存储过程获取入参出参(顺序,名字,类型,入参/出参)

    调用SQL语句 PROCEDURE NAME为过程名 自行替换要查询的过程名 POS为参数位置 NAME为参数名 TYPE为参数类型 IN OUT为入参 出参 SELECT A POSITION POS A ARGUMENT NAME NA
  • ABB 120 六轴机械手臂编程调试(三)

    下一步进行机械手臂的程序编写 程序只是进行简单的点位运动 实现抓取功能 程序控制的点位表 输入点位 点位描述 输出点位 点位描述 DI5 夹取完成 DO5 夹取物料 DI6 放料完成 DO6 放下物料 DI7 回原点 DO7 设备就绪 DI
  • Python练习——基础练习题2

    因为控制台会让不断输入 索性就把input放到注释里了 这一片主要练习if判断和while循环 初级 判断下列语句的打印结果 1 print True and True or True 2 print True and True or Fa
  • 因果图分析法例子

    某软件规格说明书包含这样的要求 第一列字符必须是A或B 第二列字符必须是一个数字 在此情况下进行文件的修改 但如果第一列字符不正确 则给出信息L 如果第二列字符不是数字 则给出信息M 解答 1 根据需求 分析出原因和结果如下 原因 1 第一
  • smbms(超市管理系统)源码 + 分析

    在项目开始之前 我们首先要对项目的整体架构分析一下 该项目一共分为四个模块 登录注销 用户管理 订单管理 供应商管理 其中用户管理 订单管理以及供应商管理都是需要对数据库进行crud的 项目的整体架构图如下 1 前期准备 1 项目架构 2
  • Android中Activity跳转到具体的Fragment的方法

    1 首先在需要跳转的Activity写此代码 Intent intent new Intent from MainActivity class intent addFlags Intent FLAG ACTIVITY SINGLE TOP
  • 理解Android上下文Context

    Context使用场景总的来说分为两大类 使用Context调用方法 比如启动Activity 访问资源 调用系统级服务等 调用方法时传入Context 比如弹出Toast 创建Dialog等 Activity Service和Applic