Android Context 详解

一、什么是Context?

Context是一个抽象基类。在翻译为上下文,是提供一些程序的运行环境基础信息。

Context下有两个子类,ContextWrapper是上下文功能的封装类(起到方法传递的作用,主要实现还是ContextImpl),而ContextImpl则是上下文功能的实现类。

ContextWrapper又有三个直接的子类,ContextThemeWrapperServiceApplication。其中,ContextThemeWrapper是一个带主题的封装类,所说的主题就是指在AndroidManifest.xml中通过android:theme为Application元素或者Activity元素指定的主题,而它有一个直接子类就是Activity,所以ActivityService以及Application的Context是不一样的,只有Activity需要主题,Service不需要主题。

其中最核心的类就是ContextImpl类。ContextImpl类继承了抽象类Context并且实现所有的抽象方法,并且自身还实现了很多get,create,check开头的方法。

在我们的实际开发中,context会被大量的使用到,例如startActivity,访问资源,toast弹出,dialog,启动service,发送广播等等。

TextView tv = new TextView(getContext());
ListAdapter adapter = new SimpleCursorAdapter(getApplicationContext(), ...);
AudioManager am = (AudioManager) 
getApplicationContext().getContentResolver().query(uri, ...);
getContext().getResources().getDisplayMetrics().widthPixels * 5 / 8;
getContext().startActivity(intent);
getContext().startService(intent);
getContext().sendBroadcast(intent);

一个应用程序进程中有多少个 Context ,这个数量等于 Activity 和 Service 的总个数加 1,1指的是 Application 的数量。

但是ContextImpl在IDE里是看不到源码的,ContextImpl的实现不会暴露给使用者,,它的位置在framework里

二、Context的子类及其作用

Context一共有三种类型,分别是ApplicationActivityService

这三个类虽然分别各种承担着不同的作用,但它们都属于Context的一种,而它们具体Context的功能则是由ContextImpl类去实现的,因此在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。

不过有几种场景比较特殊,1、比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是系统级别吐司),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。

2、再比如我们用ApplicationContext去启动一个LaunchMode为standard的Activity的时候会报错,因为这时候的ApplicationContext,它没有任务栈啊,解决这个问题的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就为它创建一个新的任务栈。

上面两个场景经常会在开发中一不小心就中招了。

三、Context的获取

1.View.getContext,返回当前View对象的Context对象,通常是当前正在展示的Activity对象。

例如我们在adapter里面

 2.Activity.getApplicationContext,获取当前Activity所在的(应用)进程的Context对象,通常我们使用Context对象时,要优先考虑这个全局的进程Context。

getApplication和getApplicationContext的区别。如果你在Activity里面打印这个两个方法的话,得到对象内存地址是一样的,也就是这两个方法获取的对象是一样的?实际上,这两个获取到的对象就是同一个对象,Application本身就是一个Context,所以这里获取getApplicationContext()得到的结果就是Application本身的实例。他们只是可以使用的范围是不一样的

getApplication这个方法一看就知道是用来获取Application实例的,只有Activity和Service才有,要想在别的地方获取Application就只能通过getApplicationContext获取(例如广播)。 

3.ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰之前的Context(用得少,不介绍)

四、Context内存泄漏

这个context的内存泄漏在app开发中随处可见,一个activity本来应该被回收了,但是因为内部类啊,或者有些静态方法的引用导致无法被回收。

所以说,我们应当

  • 当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
  • 不要让生命周期长于Activity的对象持有到Activity的引用。
  • 尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。

五、Context的创建源码分析

之前的文章里,提到了application,activity和service的创建流程,其实都是通过反射后创建application,activity和service对象的同时创建了一个ContextImpl对象,并与之关联。

详细源码我就不粘出来了,具体可看我之前的文章里

Android App启动流程和源码详解-CSDN博客

Android Service 启动流程-CSDN博客

简要流程:

1.application

  • ActivityThread.main方法--> ActivityManagerService.bindApplication方法 --> ActivityThread.handleBindApplication --> 创建Instrumentation,创建Application;

  • 每个应用进程对应一个Instrumentation,对应一个Application;
  • Instrumentation与Application都是通过java反射机制创建;
  • Application创建过程中会同时创建一个ContextImpl对象,并建立关联;

2.acticity

  • Activity中创建ContextImpl对象的具体实现在ActivityThreadperformLauncherActivity方法中;

  • Activity的创建伴随着ContextImpl的创建,二者相互持有对方的引用;

3.service

  • ActivityThread的main方法走到thread.attach(false);

  • 调用mgr.attachApplication(mAppThread);方法,熟悉吧

  • 实际上调用ActivityManagerService的attachApplication(),再调用attachApplicationLocked方法,这里面创建application,activity和service

  • 关于service,他会走到 didSomething |= mServices.attachApplicationLocked(app, processName);执行service的后续操作,mServices是ActiveServices,走到attachApplicationLocked方法里,

  • 调用app.thread.scheduleCreateService(),这不就来了。

  • ActivityThread里的scheduleCreateService通过sendMessage(H.CREATE_SERVICE, s);发送创建Service的消息
  • 在handler的handleMessage里走到handleCreateService()

核心代码:

private void handleCreateService(CreateServiceData data) {
    Service service = null;
    try {
        //(1)通过类加载器来加载 Service 对象
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance();
    } catch (Exception e) {
        //......
    }
    //(2)这里创建 ContextImpl 对象
    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
    context.setOuterContext(service);
    Application app = packageInfo.makeApplication(false, mInstrumentation);
    service.attach(context, this, data.info.name, data.token, app,ActivityManagerNative.getDefault());
    //(3)这里调用 Service 的 onCreate 方法
    service.onCreate();
    mServices.put(data.token, service);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

操作系统实验三 可变分区内存分配首次适应算法模拟

实验三 可变分区内存分配首次适应算法模拟 实验内容 模拟内存分配,了解并掌握动态分区分配中所用的数据结构、分区分配算法,深刻理解首次适应内存分配算法。 模拟实现可变分区内存分配首次适应算法;空闲分区表要求有空闲块的起始地址、大小…

6.2 Go 切片(Slice)

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

惠海半导体 30V-60V-100V-150VMOS管 打火机、加湿器NMOS管 高耐压

MOS管的工作原理 MOS管,即金属-氧化物-半导体场效应晶体管,是一种重要的电子元件,在电路中起着关键的作用。其工作原理主要基于半导体材料的特性以及电场对电荷的控制。 首先,MOS管的基本结构包括源极、栅极和漏极。其中&#xf…

JUC框架(Future CompletableFuture详解)

文章目录 FutureFuture介绍Future原理Future代码示例 CompletableFutureCompletableFuture特点应用场景方法特性方法刨析supplyAsync/runAsyncthenApply / thenApplyAsync异步回调thenAccept / thenRunexceptionallywhenCompletehandle 实现场景 更多相关内容可查看 Future Fu…

实操专区-第15周-课堂练习专区-漏斗图与金字塔图

实操专区-第15周-课堂练习专区-漏斗图 下载安装ECharts,完成如下样式图形。 代码和截图上传 基本要求:下图3选1,完成代码和截图 完成 3.1.3.16 漏斗图中的任务点 基本要求:2个选一个完成,多做1个加2分。 请用班级学号姓…

有趣的css - 列表块加载动效

大家好,我是 Just,这里是「设计师工作日常」,今天分享的是用 css 打造一个极简的列表块加载动效。 最新文章通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码css 部分代码 完整代码如下html 页面css 样式页面渲染效果 整…

Linux shell命令

cat 文件名 查看文件内容, tac文件名 倒着显示。 more 文件名 显示内容 less文件名 和more的功能一样,按上下左右键,按Q键结束。 head文件名,只显示前10行内容。 ln是一个默认创建硬链接的命令 ln 文件名 ls -i文件名…

【每日力扣】300. 最长递增子序列 与 139. 单词拆分

🔥 个人主页: 黑洞晓威 😀你不必等到非常厉害,才敢开始,你需要开始,才会变的非常厉害 300. 最长递增子序列 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&…

JEPaaS 低代码平台 accessToTeanantInfo SQL注入漏洞复现

0x01 产品简介 JEPaaS低代码开发平台开源版 旨在帮助企业快速实现信息化和数字化转型。该平台基于可视化开发环境,让软件开发人员和业务用户通过直观的可视化界面来构建应用程序 ,而不是传统的编写代码方式。 用户可以在开发平台灵活各个图形化控件,以构建业务流程、逻辑和…

Linux基础学习笔记

目录 1、Linux安装 1.1 安装教程 1.2 Linux目录结构 2、Linux常用命令 2.1 ls 2.2 命令分类 2.3 目录处理命令 2.4 操作文件命令 2.5 查找文件命令 2.6 ln链接命令 2.7 进程相关命令 ​编辑3、配置网络 3.1 关闭windows防火墙 3.2 配置好虚拟机的局域网 3.3 配置…

电焰灶:引领未来厨房烹饪革命,创“灶”家庭幸福感和安全感

随着科技的飞速发展,厨房灶具也迎来了前所未有的革新。电焰灶,这一新型的灶具,正以其独特的优势,逐渐取代传统的燃气灶和电磁炉,开启了一场灶具的新时代。它以其方便、节能的特点,让烹饪变得更加轻松高效&a…

ITIL4认证考试这么贵,还值得考证吗,有必要学吗?

从2023年4月1日开始,ITIL 4是Foundation认证将会捆绑OTM(Official Training Materials),这样在一次ITIL4的考试费中将会捆绑:试卷费电子教材书费监考费OTM费,每一种考试费都相较于2022年有涨幅,再加上PeopleCert收取的授权机构的授…

【喜报】科大睿智多家服务企业上榜2024年第四批DCMM名单

近日,DCMM官方平台发布通知公告,根据《数据管理能力成熟度评估工作管理办法(暂行)》的有关规定,经单位自愿申请,评估机构评估、专家评审及公示,下列27单位获得数据管理能力成熟度等级证书。小编祝贺多家服务企业上榜20…

神经网络不确定性综述(Part V)——Uncertainty measures and quality

相关链接: 神经网络不确定性综述(Part I)——A survey of uncertainty in deep neural networks-CSDN博客 神经网络不确定性综述(Part II)——Uncertainty estimation_Single deterministic methods-CSDN博客 神经网络不确定性综述(Part III)——Uncertainty est…

探索 ChatboxAI:智能对话的新时代

在人工智能迅速发展的今天,智能对话已经成为了我们日常生活中不可或缺的一部分。从智能助理到聊天机器人,AI 技术正在改变我们与世界互动的方式。今天,我们要介绍的是一个全新且功能强大的平台——ChatboxAI。 什么是 ChatboxAI?…

PyTorch自定义张量操作开发指南【CFFI+CUDA】

PyTorch 与 TensorFlow 一起成为深度学习研究人员和从业者的标准。虽然 PyTorch 在张量运算或深度学习层方面提供了多种选择,但一些专门的操作仍然需要手动实现。在运行时至关重要的情况下,应使用 C 或 CUDA 来完成此操作,以支持 CPU 和 GPU …

智能除螨—wtn6040-8s语音芯片方案引领除螨仪新时代

语音螨仪开发背景: 随着物联网技术的快速发展,除螨仪作为家庭清洁的重要工具,其智能化、人性化的设计成为提升市场竞争力的关键。置入语音芯片的除螨仪,通过开机提示、工作状态反馈、操作指引、故障提醒等内容。用户可以更加直观…

.NET 某和OA办公系统全局绕过漏洞分析

转自先知社区 作者:dot.Net安全矩阵 原文链接:.NET 某和OA办公系统全局绕过漏洞分析 - 先知社区 0x01 前言 某和OA协同办公管理系统C6软件共有20多个应用模块,160多个应用子模块,从功能型的协同办公平台上升到管理型协同管理平…

腾讯社招测试岗有点奇葩的面试,被问抽奖程序的测试用例设计

今天腾讯网上预约社会招聘,我是前天才看到这条消息,前天投了简历,还叫别人内推了我一把,但是悲剧的我把简历上的号码写成了原来在北京的号码,所以我也不知道是别人觉得我简历不合适还是因为联系不上我所以没有邀请我参…

View->Bitmap缩放到自定义ViewGroup的任意区域

Bitmap缩放和平移 加载一张Bitmap可能为宽高相同的正方形,也可能为宽高不同的矩形缩放方向可以为中心缩放,左上角缩放,右上角缩放,左下角缩放,右下角缩放Bitmap中心缩放,包含了缩放和平移两个操作&#xf…