理解android中最熟悉的Context

程序员通勤双肩包,下单送全年 CSDN 会员 防泼水面料电脑包,保护笔记本电脑,背负轻便,下单再赠 Coding Plan,看技术干货、查文档、下载源码,一站式搞定学习需求 阅读详情

Context的介绍

在这里插入图片描述
Context 在Android开发中几乎无处不在,对于开发来说实在是再熟悉不过了。但是你真的了解它吗?是否在使用的时候分不清楚呢?并且可能你的一不小心就会导致内存泄漏。

由于Android中存在不同类型的Context,因此作为Android开发,我们可能刚开始不知道在某个位置使用哪个上下文。所以,我们看看下面是如何正确使用Context的。

其中android主要有两种类型的上下文:

Application Context:这是一个单例,可以在activity中使用getApplicationContext()来获取,它与应用程序的生命周期相关。

Activity Context:这是Activity。例如-MainActivity。它仅是MainActivity的实例。

Context提供了关于应用环境全局信息的接口。它是一个abstract类,它的执行被Android系统提供,允许获取以应用为特征的资源和类型,是一个统领一些资源APP环境变量等的上下文(包括应用级别操作,如启动Activity,发广播,接收intent等)。abstract会有它的实现类。在源码中,我们可以通过AndroidStudio去查看它的子类,得到以下关系:
它有2个具体实现子类:ContextImpl、ContextWrapper。
在这里插入图片描述
其中ContextImpl是Context的具体是实现类,而ContextWrapper则是Context的包装类。

Activity、Application、Service都继承自ContextWrapper(Activity继承自ContextWrapper的子类ContextThemeWrapper),但它们的初始化过程中都会创建ContextImpl对象,由ContextImpl实现Context中的方法。

在app中有多少个Context

Context数量 = Activity数量 + Service数量 + 1

上面的1代表着Application的数量,因为一个应用程序中可以有多个Activity和多个Service,但是只能有一个Application。

Context 的作用域

不是随便获取一个Context实例就可以的,它的使用有一些规则和限制。因为Context的具体实例是由ContextImpl类去实现的,因此,Activity、Service、Application 3种类型的Context都是等价的。但是,需要注意的是,,有些场景,比如启动Activity、弹出Dialog等。为了安全,Android不允许Activity或者Dialog凭空出现,一个Activity的启动肯定是由另一个Activity负责的,也就是以此形成的返回栈,而Dialog 则必须是在一个Activity上弹出(系统Alert类型的Dialog除外),这种情况下, 我们只能用Activity类型的Context,否则报错。

Activity继承自ContextThemeWrapper,而Application和Service继承ContextWrapper,所以ContextThemeWrapperContextWrapper的基础上作了一些操作,使得Activity更加牛逼。

在这里插入图片描述
关于表格中提到的Application和Service不推荐的2种情况:

如果用ApplicationContext去启动一个LaunchMode为standard的Activity的时候会报错:

androud,util.AndroidRuntimeException:Calling startActivity from outside of an Activity context require the FLAG_ACTIVITY_NEW_TASK flag。Is this really what you want?

翻译一下,并了解这个FLAG的都知道,此时的非Activity类型的Context并没有所谓的返回栈,因此带启动的Activity就找不到栈。它还给我们明确之处了FLAG的解决办法,这样启动的时候就为它创建一个新的任务栈,而此时Activity是以Single Task模式启动的。所以这种用Application Context启动Activity的方式不推荐,Service同理。
在Application和Service中去layout inflate也是合法的,但是会使用系统默认的主题样式,如果自定义了某些样式可能不会被使用,所以也不推荐。

注:和UI相关的,都应该使用Activity Context来处理。其他的一些操作,Service、Activity、Application等实例都是可以的。
同时要注意Context的引用持有,防止内存泄漏。可在被销毁的时候,置Context为null。

如何获取Context对象

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

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

3.ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰之前的Context。实际开发很少用,也不建议使用。

4.Activity.this:返回当前Activity的实例,如果UI控件需要使用Activity作为Context对象,但默认的Toast实际上使用的ApplicationContext也可以。

实现View.OnClick监听方法中,写Toast,不要用this,因为this,在onClick(View view)指的是view对象而不是Activity实例,所以在这个方法中,应该使用”当前的Activity名.this“,这是入门者比较容易混淆的地方。

getApplication()和getApplicationContext():

获取当前Application对象用getApplicationContext.但是getApplication又是什么。

Application app=(Application)getApplication();
Log.e(TAG,"getApplication is "+app);
Context context=getApplicationContext();
Log.e(TAG,"getApplicationContext is "+ context);

运行后看logcat。从打印结果可以看出它们2个的内存地址是相同的,即它们是同一个对象。 但是这2个方法在作用域上有比较大的区别。

getApplication()一看就知道是用来获取Application实例的(道理可以联想getActivity())。

但getApplication()只有在Activity和Service中才能调用的到。

对于比如BroadcastReceiver等中也想要获取Application实例,这时就需要getApplicationContext()方法。

//继承BroadcastReceiver并重写onReceive()方法
@Override
public void onReceive(Context context.Intent intent){
	Application app=(Application)context.getApplicationContext();
}

我们经常会遇到内存泄漏,比如Activity销毁了,但是Context还持有该Activity的引用,造成了内存泄漏。(经常遇到)

2种典型的错误引用方式:

错误的单例模式:

public class Singleton{
	private static Singleton instancel
	private Context context;
	private Singleton(Context context){
		this.context=context;
	}
	public static Singleton getInstance(Context context){
		if(instance == null ){
			instance=new Singleton(context);
		}
		return instance;
	}
}

熟悉单例模式的都知道,这是一个非线程安全的单例模式,instance作为静态对象,其生命周期要长于普通的对象(单例直到APP退出后台才销毁),其中也包含了Activity。比如Activity A去getInstance()得到instance对象,传入this,常驻内存的Singleton保存了我们传入的A对象,并一直持有,即使Activity被销毁掉,但因为它的引用还存在于一个Singleton中,就不可能被GC(垃圾回收)掉,这样就导致了内存泄漏。比如典型的数据库操作,存储数据,需要重复的去索取数据,用单例保持数据和拿到Activity持有context引用,因为单例可以看作是上帝,它帮我们保存数据。所以即使Activity被finish掉,还有它的引用在Singleton中。

View持有Activity引用:

public class MainActivity extend Activity{
	private static Drawable mDrawable;	
	@Override
	protected void onCreate(Bundle saveInstanceState){
		super.onCreate();
		setContentView(R.layout.activity_main);
		ImageView imageview=new ImageView(this);//通过代码动态的创建组件,而不是传统的xml配置组件,这里的ImageView持有当前Activity的引用。
		mDrawable=getResources().getDrawable(R.drawable.ic_launcher);
		imageview.setImageDrawable(mDrawable);
	}
}

上述代码中,有一个static的Drawable对象。当ImageView设置这个Drawable的时候,ImageView保存了这个mDrawable的引用,而ImageView初始化的时候又传入了this,此处的this是指MainActivity的context。因为被static修饰的mDrawable是常驻内存的(比类还要早加载)。MainActivity是它的间接引用了,当MainActivity被销毁的时候,也不能被GC掉,就造成了内存泄漏。

如何在程序中正确的使用Context:

一般Context造成的内存泄漏,几乎都是当Context销毁的时候,因为被引用导致销毁失败。而Application的Context对象可以简单的理解为伴随着进程存在的(它的生命周期也很长,毕竟APP加载的时候先加载Application,我们可以自定义Application然后继承系统的Application)。
正确使用:

1.当Applicatin的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context;
2.不要让生命周期长于Activity的对象持有Activity的引用。

3.尽量不要在Activity中使用非静态内部类。非静态内部类会隐式持有外部类实例的引用。如果使用静态内部类,将外部实例引用作为弱引用持有。

面试:

ContextImpl 实例是什么时候生成的,在 Activity 的 onCreate 里能拿到这个实例吗

先说结论,可以。我们开发的时候,经常会在 onCreate 里拿到 Application,如果用 getApplicationContext 取,最终调用的就是 ContextImpl 的 getApplicationContext 方法,如果调用的是 getApplication 方法,虽然没调用到 ContextImpl ,但是返回 Activity 的成员变量 mApplication 和 ContextImpl 的初始化时机是一样的。

再说下它的原理,Activity 真正开始启动是从 ActivityThread.performLaunchActivity 开始的,这个方法做了这些事:

通过 ClassLoader 去加载目标 Activity 的类,从而创建 对象

从 packageInfo 里获取 Application 对象

调用 createBaseContextForActivity 方法去创建 ContextImpl

调用 activity.attach ( contextImpl , application) 这个方法就把 Activity 和 Application 以及 ContextImpl 关联起来了,就是上面结论里说的时机一样

最后调用 activity.onCreate 生命周期回调

通过以上的分析,我们知道了 Activity 是先创建类,再初始化 Context ,最后调用 onCreate , 从而得出问题的答案。不仅 Activity 是这样, Application 、Service 里的 Context 初始化也都是这样的。

总结:

Context类仅仅是定义了一组抽象方法的抽象类,其内部的方法真正实现的地方都在ContextImpl类中。

ContextWrapper是Context的一个包装类,其里面所有的方法实现都是调用其内部mBase变量的方法,而mBase就是ContextImpl对象。然而ContextWrapper还有一个ContextThemeWrapper子类,该类中扩展了主题相关的方法。Application和Service是继承自ContextWrapper,而Activity是继承自ContextThemeWrapper,Activity在启动的时候系统都会加载一个主题,也就是我们平时在AndroidManifest.xml文件里面写的android:theme=”@style/AppTheme”属性!然而Service和Applicaton都和UI界面并没有卵关系!因此它们继承自ContextWrapper。所以Activity,Application,Service其实都关联着一个mBase变量,而mBase变量是ContextImpl对象的赋值,也是真正实现抽象类Context的地方。

ContextImpl 实现了抽象类Context里面的所有方法,获取资源,启动Activity,Service等。值得注意的是在ContextImpl创建的时候就会利用静态区来注册系统的各种服务,因此每个持有Context引用的类都可以通过getSystemService来轻松的获取系统服务了。比如我们平时LayoutInflater类来加载一个XML布局时,LayoutInflater布局加载器也是调用context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
ok,context终于讲解完了,以后我们就能够舒舒服服的使用context的啦!如果觉得学到了东西欢迎点个赞咯!!嘻嘻

本次参考


参考链接1

参考链接2

参考链接3

参考链接4

参考链接5

参考链接6

Android Context 详解 Context是一个抽象基类。在翻译为上下文,是提供一些程序的运行环境基础信息。Context下有两个子类,是上下文功能的封装类(起到方法传递的作用,主要实现还是ContextImpl),而则是上下文功能的实现类。又有三个直接的子类,Service和。其中,是一个带主题的封装类,所说的主题就是指在AndroidManifest.xml中通过android:theme为Application元素或者Activity元素指定的主题,而它有一个直接子类就是Activity,所以Activity和Service。 阅读详情

相关推荐

Android四大组件系列11 深入理解Context

一 概述 Context 相信几乎所有的 Android 开发人员基本上都非常熟悉,因为它太常见了。有大量的场景都离不开 Context,下面列举部分常见场景: 启动 Activity (startActivity) 启动服务 (startService) 发送广播 (sendBroadcast),注册广播接收者 (registerReceiver) 获取 ContentResolver (getContentResolver) 获取类加载器 (getClassLoader) 打开或创建数据库 (openO

liuwg1226的专栏 2480

Android Studio开发基础之Context用法说明

1、Context说明               Context是一个用于访问全局信息的接口,如应用程序的资源(如图片,字符串等),一些常用的组件继承自Context,如Activity和Service等等。        如利用Java代码创建一个textView,textView的第一种setText()方法直接传入一个字符串,第二种方法传入一个整形的id(位于values\\strin

智慧空间实验室 1万+

Android插件化——深入理解Context机制

1、

浅谈Android 926

Android Context详解(全解析)

我们常用的Activity,Service,Application都是Context的子类。所以知道Context的具体实现是非常有必要的。 下面是Context的体系结构图: Context本身是一个抽象类。他的实现类是ContextImpl。而ContextWrapper是一个包装类(装饰设计模式)。在我们用IDE查看Context继承关系的时候,我们是不能直接看到ContextImpl这个类的,通过这种手段让程序员不能直接看到ContextImpl这个类,而只能看到ContextWrapper这个

ScottePerk的博客 1万+

深度详解 AndroidContext

Android 开发中、亦或是面试中都离不开四大组件的身影,而在创建或启动这些组件时,并不能直接通过 new 关键字后跟类名来创建实例对象,而是需要有它们各自的上下文环境,也就是本篇文章要讨论的 ContextContext 提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被 Android 系统所提供。它允许获取以应用为特征的资源和类型,是一个统领一些资源(应用程序环境变量等)的上下文。............

neuHenry 9154

AndroidContext理解

Context对于Android开发人员来说并不陌生,项目中我们会经常使用Context来获取APP资源,创建UI,获取系统Service服务,启动Activity,绑定Service,发送广播,获取APP信息等等。那么Context到底是什么?Context又是怎么来实现以上功能的?在什么场景下使用不同的Context?一个APP中总共有多少个Context?这篇博客将从源码角度带你分析Andr...

持之以恒 9890

Android 进阶】理解 Context

Context,即上下文,是Android中常用的类之一。但是很多人仅停留在了“会用”这一阶段,没有做到“知其然,知其所以然”。本篇文章将较为详细的为大家介绍一下Context,帮助大家更加深入的理解Android程序员的这一“老朋友”。

qq_45254908的博客 3057

Android 理解Context

Context可以理解为“上下文”或”环境“,它提供了一个应用运行所需要的信息,Context 参与加载资源、启动Activity、启动Service、获取系统服务/应用资源、创建View、数据库等操作。

xiangxiongfly 1562

Android 进阶解密——如何理解Context

Android 开发中,我们随处看见Context,但你知道它到底是代表了什么吗?Context,即上下文,是Android中常用的类之一。Android开发中,从启动 Activity,访问资源,再到弹出一个Toast,创建一个Dialog,Context 实可谓无处不在。本篇文章将较为详细的为大家介绍一下Context,帮助大家更加深入的理解Android程序员的这一“老朋友”。 …Context 也就是上下文对象,是 Android 常用的类。Context 意为上下文,是一个应用程序环境信息的接

bugyinyin的博客 2511

理解Android Context

一. 概述 接触过Android的小伙伴, 一定不会对Context感到陌生, 有大量的场景使用都离不开Context, 下面列举部分常见场景: 启动Activity (startActivity) 启动服务 (startService) 发送广播 (sendBroadcast), 注册广播接收者 (registerReceiver) 获取ContentResolver (getCont...

淡淡的宁静的博客 2157

Android——context理解

Context的中文翻译为:语境; 上下文; 背景; 环境,在开发中我们经常说称之为“上下文”在语文中,我们可以理解为语境,在程序中,我们可以理解为当前对象在程序中所处的一个环境,一个与系统交互的过程。 比如微信聊天,此时的“环境”是指聊天的界面以及相关的数据请求与传输, Context在加载资源、启动Activity、获取系统服务、创建View等操作都要参与。1.1context是什么?一个A

tingzuhuitou的博客 1445

AndroidContext理解

Activity的Context不可用于长期存活对象(如静态变量),应优先使用Application Context。:Application(全局)、Activity(界面)、Service(后台),各自作用域与生命周期不同。:真正的Context实现类,由系统创建并注入到ContextWrapper中,承担具体功能实现。:Activity的父类,增加了主题相关的资源访问能力。,负责启动应用的主线程消息循环,并初始化应用的核心环境。:提供访问应用资源(如字符串、布局、图片)的入口,通过。

qq_45906596的博客 1169

理解 Android Context

1. 概述1.1 Context1.2 ContextWrapper1.3 ContextThemeWrapper1.4 Application1.5 Service2. 组件初始化 -- Activity3. 创建ContextImpl3.1 createBaseContextForActivity3.2 createAppContext3.4 ContextImpl初始化4. Context attach过程5. 总结5.1 组件初始化5.2 Context attach过程5.3 Context使.

怪咖先森的博客 4804

Android深入理解Context--Application中Context的创建过程

我们分为四篇文章详细介绍Context, 这是第一篇 1 Android深入理解Context–Application中Context的创建过程 2 3 4 前言 Context是我们在开发中经常使用到的一个类,它的功能非常丰富,但是很多人可能仅仅知道怎么用, 或者说是比较迷茫的使用, 不知道Context内部的逻辑,这一次我们就从源码的角度来分析一下Context。 在讲解之前,首先抛出...

周将的博客 3055

Android Context 源码的理解

Context 是什么 类的剖析 Context ContextImpl ContextWrapper ContextThemeWrapper Context 创建的时机 Application 的创建 Activity 的启动 Service Context 的数量 不同 Context 的分析 Application 中的 Context Activity 中的Context Serv

programming_path的博客 1376

Android Context完全解析,你所不知道的Context的各种细节

Context相信所有的Android开发人员基本上每天都在接触,因为它太常见了。但是这并不代表Context没有什么东西好讲的,实际上Context有太多小的细节并不被大家所关注,那么今天我们就来学习一下那些你所不知道的细节。我们知道,Android应用都是使用Java语言来编写的,那么大家可以思考一下,一个Android程序和一个Java程序,他们大的区别在哪里?划分界限又是什么呢?其实简单点分析,Android程序不像Java程序一样,随便创建一个类,写个main()方法就能跑了,而是要有一个完整的

郭霖的专栏 17万+

AndroidContext理解

这里记录Context的原因是新来的同事问我Android Context 怎样理解,我是这样说的,Context 英文是上下文,它是一个抽象的类,加入在MainActivity 中,Context 就是this 或者MainActivity.this 也就是当前的Activity, 说完之后感觉和没说,没什么区别,好吧,自己感觉当时也挺不好意思的,为了更好的理解Context,我把项目中的一个util 给你说了下,下面是希望能帮助更多的Android 新人理解Context 下面是一个Util .

小牧在一直在学习,在前进的道路上大家一起学习,进步。 1029

Android 基础—— 对Context理解与使用技巧

一、Context 基础概念       1、什么是Context             Context 类,时刻的在与它打交道,例如:Service、BroadcastReceiver、Activity等都会利用到Context的相关方法。但是不懂Context的原理、类结构关系。     1) Context是一个抽象类,其通用实现在ContextImpl类中。     2) Con

知秋一叶 2737

Android Context上下文理解

Android中有个我们熟悉又陌生的对象Context(上下文),当我们启动Activity的时候需要上下文,当我们使用dialog的时候我们需要上下文,但是上下文对象到底是个什么东西呢?   在Android api当中是这样描述context对象的。  "Interface to global information about an application environment.

acaoye的专栏 1496
上一篇: java和kotlin如何相互转化
下一篇: 【算法】已知一个搜索二叉树后序遍历的数组posArr,根据posArr重建树
菜鸟码农阿庆
博客等级 码龄10年 61粉丝 69原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值