理解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 - 保存动态更改布局的状态

    我有一个布局 用户可以在其中添加按钮并将其放置在他们想要的位置 我想允许用户保存他们的布局 以便下次打开应用程序时加载它 有谁知道我是否可以将文件保存到 SD 卡上 或者 我可以使用某种layout getXml 方法并将其放入我的应用程序
  • Firebase 管理 SDK Android

    在 Android 中初始化 Firebase Admin SDK 的代码 Override protected void onCreate Bundle savedInstanceState super onCreate savedIns
  • BottomNavigationView - 如何获取选定的菜单项?

    我使用BottomNavigationView来切换片段 如何获取当前选定的菜单项 以防止重新打开片段 BottomNavigationView bottomNavigationView BottomNavigationView findV
  • 导航组件重复 NavArgs 的问题

    我有一个片段 class SomeFragment private val args by navArgs
  • Android SoundPool 堆限制

    我正在使用 SoundPool 加载多个声音剪辑并播放它们 据我所知 它的功能 100 正确 但在 load 调用期间 我的日志中充斥着以下内容 06 09 11 30 26 110 ERROR AudioCache 23363 Heap
  • Android libgdx 首选项丢失

    我在 Libgdx 引擎中创建了 Android 游戏 一段时间后 我注意到在某些应用程序杀手中杀死该应用程序后 或者如果我在 Android 设置中执行 强制关闭 操作 我保存到首选项中的游戏选项就会丢失 有办法防止这种情况吗 我从来没有
  • Delphi XE7 Android 全屏(隐藏软键)

    如何在XE7中全屏显示 隐藏顶部 标题 和底部 软键 工具栏 在 XE6 中 我可以通过在应用程序部分写入来调整 AndroidManifest 以使我的应用程序全屏显示并且没有操作栏 android theme android style
  • 如果我们使用后退按钮退出,为什么 Android 应用程序会重新启动?

    按住主页按钮并返回应用程序时 应用程序不会重新启动 为什么使用后退按钮会重新启动 如果我们使用后退按钮退出 有什么方法可以解决在不重新启动的情况下获取应用程序的问题吗 请帮忙 当您按下Home按钮 应用程序将暂停并保存当前状态 最后应用程序
  • 已经使用 AsyncTask doInBackground 但新数据未显示

    我使用 AsyncTask 创建一个聊天室来接收消息 因此它总是检查即将到来的消息并将其显示给客户端 但代码似乎无法按我希望的方式工作 在客户端只显示所有旧数据 新数据不显示 因为当我尝试从服务器发送消息时 新数据没有显示在客户端中 我对这
  • Android 原理图内容提供程序库配置?

    Jake Wharton 在最近的一次演讲中提到了这个库 它看起来是避免大量样板文件的好方法 所以我尝试了一下 但没有任何成功 https github com SimonVT schematic https github com Simo
  • Android 版 Robotium - solo.searchText () 不起作用

    我在使用 Robotium 时遇到 searchText 函数问题 我正在寻找这个字符串
  • minHeight 有什么作用吗?

    在附图中 我希望按钮列与图像的高度相匹配 但我也希望按钮列有一个最小高度 它正确匹配图像的高度 但不遵守 minHeight 并且会使按钮向下滑动 我正在为按钮列设置这些属性
  • 如何在C(Linux)中的while循环中准确地睡眠?

    在 C 代码 Linux 操作系统 中 我需要在 while 循环内准确地休眠 比如说 10000 微秒 1000 次 我尝试过usleep nanosleep select pselect和其他一些方法 但没有成功 一旦大约 50 次 它
  • 材质设计图标颜色

    应该是哪种颜色 暗 材质图标 在官方文档上 https www google com design spec style icons html icons system icons https www google com design s
  • 调节麦克风录音音量

    我们正在尝试调整录音时的音量级别 麦克风似乎非常敏感 会接收到很多静电 我们查看了 setVolumeControlStream 但找不到传入其中来控制麦克风的流 将您的音频源设置为 MIC using MediaRecorder Audi
  • 如何创建像谷歌位置历史记录一样的Android时间轴视图?

    我想设计像谷歌位置历史这样的用户界面 我必须为我正在使用的应用程序复制此 UIRecyclerView 每行都是水平的LinearLayout其中包含右侧的图标 线条和视图 该线是一个FrameLayout具有圆形背景和半透明圆圈Views
  • Android 设备上的静默安装

    我已经接受了一段时间了 在 Android 上静默安装应用程序是不可能的 也就是说 让程序安装捆绑为 APK 的应用程序 而不提供标准操作系统安装提示并完成应用程序安装程序活动 但现在我已经拿到了 Appbrain 快速网络安装程序的副本
  • 如何将设备连接到Eclipse?

    我无法解决这个简单的问题 我正在尝试通过 USB 电缆将我的设备连接到 Eclipse 在我的 PC 上 我已经安装了 Eclipse 和 Android SDK 并且在模拟器上运行该程序运行良好 我已在我的电脑上下载并安装了 Samsun
  • Android - 将 ImageView 保存到具有全分辨率图像的文件

    我将图像放入 ImageView 中 并实现了多点触控来调整 ImageView 中的图像大小和移动图像 现在我需要将调整大小的图像保存到图像文件中 我已经尝试过 getDrawingCache 但该图像具有 ImageView 的大小 我
  • android Accessibility-service 突然停止触发事件

    我有一个 AccessibilityService 工作正常 但由于开发过程中的某些原因它停止工作 我似乎找不到这个原因 请看一下我的代码并告诉我为什么它不起作用 public class MyServicee extends Access

随机推荐

  • 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