Android中的Context(一)
Android中的Context(一)
在Android开发中,Context可以说是我们接触地非常多的一个概念了,也译作“上下文”,但是这个上下文到底是什么却并不好理解。
通俗的理解Context是什么:
广义上的程序开发来说,每一段程序都可能有很多外部依赖(比如说外部变量),一旦程序有了这些外部依赖,在程序脱离了这些外部依赖的时候,它是没法独立运行的,为了使得程序可以运行,你需要提供这些外部依赖,而这些依赖的集合,就是上下文。或者说,Context可以理解为提供信息或功能的容器或者环境。
而之所以Context很模糊,是因为在不同的地方,Context代表着不同的含义。同阅读文章一样,上下文在不同的地方所表示的意义也是不一样的,这个需要感性的去理解。
举一个例子来说:
我们常做的阅读理解,要你答某一句话表达了鲁迅先生的什么心理。如果上下文都是省略号,仅凭一句话,要回答出问题那就只能是瞎扯了,而有了上下文,才能分析出答案是什么(程序正确执行)。
拿更具体的例子来说:
在我们点击一个Button进行页面跳转的时候,系统最起码需要保存我们是从哪个页面跳转过来的,这样才能够在点击返回的时候正确地返回。
Context在Android中使用场景:
需要注意地是,Context并不是Android特有的概念,它是计算机程序通用的概念。
可以说Context的概念贯穿了整个android体系;在Service、BroadcastReceiver、Activity等都会用到Context的相关方法。在Android的APP开发中,我们可能无时无刻不在跟Context打交道,最常见的Activity跳转,弹出Toast,访问应用的资源文件等等都用到了Context这个特殊的对象。
Android中应用程序A的Activity1调起应用程序B的Activity2,即应用A要使用应用B,Android并不是将整个程序B调起,而是直接调起Activity2,在最早学习到Activity启动模式的时候也学到过Activity的启动是由Android系统来管理的,使用者只需要提供对应的意图(在意图中需要上下文)即可。但是另一方面,Activity1和2应该是属于不同的进程,二者的地址空间是不一样的,同样A的1是无法直接使用B的2的,但是如果说,将Activity独立封装在一个上下文环境中,那么它应该就具有一定的独立运行能力,从而,它在逻辑上也就可以“直接”地被其他应用所使用,并不需要借助整个进程B才能实现使用Activity2。当然,这里仅仅是一种设想。
而在Android中,封装界面的小型上下文就是Android里的Activity,而封装服务的小型上下文就是Service。
Context类家族:
Context类位于
framework.package.android.content.Context
/**
* Interface to global information about an application environment. This is
* an abstract class whose implementation is provided by
* the Android system. It
* allows access to application-specific resources and classes, as well as
* up-calls for application-level operations such as launching activities,
* broadcasting and receiving intents, etc.
*/
public abstract class Context {
...
public abstract AssetManager getAssets();
public abstract Resources getResources();
...
从源码注释来看,Context描述了一个应用程序环境的信息(即上下文);它是一个抽象类,Android提供了该抽象类的具体实现类;通过它我们可以获取应用程序的资源和类(包括应用级别操作,如启动Activity,发广播,接受Intent等)。
通过继承来实现抽象类,而Context的继承示意图如下:
1.ContextImpl
Context直属子类之一,是Context API的通用实现,Context的功能实现类,为Activity等组件提供基础的上下文对象。
ContextImpl:(frameworks.base.core.java.android.app.ContextImpl)
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*
*/
class ContextImpl extends Context {
2.ContextWrapper:
Context的直属子类之一,是Context的封装类。
Application和Service都继承自ContextWrapper。
ContextWrapper:(frameworks.base.core.java.android.content.ContextWrapper)
/**
* Proxying implementation of Context that simply delegates all of its calls to
* another Context. Can be subclassed to modify behavior without changing
* the original Context.
*/
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
ContextWrapper内部包含有一个Context对象mBase,但是其真正的类型是ContextImpl,ContextWrapper的所有方法最终都是调用mBase对应的方法。
其内部方法的特征是:如果重写的是没返回值,则直接调用父类的此方法;如果重写的有返回值,则返回调用父类返回的值。
Context与ContextWrapper以及ContextImpl三者之间的关系是装饰模式,Context是抽象构件类,ContextImpl类是具体构件类,ContextWrapper类是抽象装饰类。
3.ContextThemeWrapper:
是一个带主题的Context封装类,Activity继承自ContextThemeWrapper。
/**
* A context wrapper that allows you to modify or replace the theme of the
* wrapped context.
*/
public class ContextThemeWrapper extends ContextWrapper {
Context的功能:
从前面的类关系图中可以看出Context类具体落实类型就三种:Activity,Service,Application三种,那么这三种类型之间在功能上有什么异同?
启动Activity
启动和停止Service
发送广播消息(Intent)
注册广播消息(Intent)接收者
可以访问APK中各种资源(如Resources和AssetManager等)
可以访问Package的相关信息
APK的各种权限管理
上图已经做了标注各自的Context能做什么,而ContentProvider和BroadCastReceiver之所以也在是因为在其内部也存在一个Context供使用。
对于No1和No2需要说明:
1.No1:启动Activity在这些类中是可以的,但是需要创建一个新的task。一般情况不推荐。
2.No2:在这些类中去layout inflate是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。
由于Context的具体功能都是由ContextImpl去实现的,而Activity,Service,Application这三种Context类的绝大多数功能最终都是通过ContextImpl来实现,所以它们的能力在绝大多数情况下是通用的。
但是处于安全考虑,Android对于一个Activity的启动必须建立在另一个Activity之上,因为要形成返回栈。而对于ShowDialog也限制在必须在Activity之上弹出(系统的Dialog除外)。
APP中的Context总数:
前面已经说过Context的类型就三种:Application,Activity,Service。
那么一个应用程序有多少个Context也就显而易见的了:
Context总数 = Application(1) + Activity(n) + Service(n)
Context获取:
获取的示例如下:
//获取当前Activity的上下文,这样获取的上下文对象可以绑定Activity的生命周期
1.Activity中的this,或者Activity.this
//获取当前应用Application的上下文
2.Application.getApplicationContext()//上下文对象绑定Application的生命周期
Application.getApplication
//从Context所属的上下文访问这个Context
3.ContextWrapper.getBaseContext()
//获取当前View所在的上下文
4.View.getContext()
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Log.e(TAG, " this====="+this);
Log.e(TAG, " Main2Activity.this====="+Main2Activity.this);
Log.e(TAG, " getBaseContext()====="+getBaseContext());
Log.e(TAG, " getApplication()====="+getApplication());
Log.e(TAG, "getApplicationContext()====="+getApplicationContext());
}
输出日志如下:
获取Application上下文的两个方法:
前面有提到过Application也是上下文的一种,那么通过getApplication和getApplicationContext这两者所获取的上下文之间又有什么区别?
从上面的输出日志我们可以看出到两个方法返回的实质上是同一个对象,至于原因暂时先不做讨论。
getApplication():
该方法由Activity或者Service提供,返回的是拥有当前Activity或Service的Application。
//代码位置:android.app.Activity.java
/** Return the application that owns this activity. */
public final Application getApplication() {
return mApplication;
}
getApplicationContext()
/**
* Return the context of the single, global Application object of the
* current process. This generally should only be used if you need a
* Context whose lifecycle is separate from the current context, that is
* tied to the lifetime of the process rather than the current component.
*/
public abstract Context getApplicationContext();
该方法返回的是当前进程的全局上下文对象,这个方法使用场景是需要一个上下文与当前上下文的生命周期相分离的时候,因为这个方法获取到的上下文是与整个应用进程的生命周期相关的,而不是仅仅局限于当前上下文的生命周期。
比如说,在注册广播接收器的时候,如果使用的是Activity的上下文,这意味着,这个接收器会在该Activity被销毁时一同撤销注册,而如果使用getApplicationContext返回的上下文对象,广播接收器将会同Application联系在一起,也就不会被用户的操作所移除。
两个方法返回的都是Application上下文对象,但是区别在于两个方法的作用域是不同的,getApplication方法只能在Activity或者Service中使用,但是需要用到Application上下文的地方却不仅仅局限于Activity或者Service,比如说广播接收器。
Context使用注意:
Context如果使用不恰当很容易引起内存泄露问题。
最简单的例子比如说引用了Context的错误的单例模式:
public class Singleton {
private static Singleton instance;
private Context mContext;
private Singleton(Context context) {
this.mContext = context;
}
public static Synchronized Singleton getInstance(Context context) {
if (instance == null) {
instance = new Singleton(context);
}
return instance;
}
}
上述代码中,我们使得了一个静态对象持有Context对象,而静态数据的生命一般是长于普通数据的,因此当Context被销毁(例如假设这里持有的是Activity的上下文对象,当Activity被销毁的时候),因为instance仍然持有Context的引用,导致Context虽然被销毁了但是却无法被GC机制回收,因为造成内存泄露问题。
而一般因为Context所造成的内存泄漏,基本上都是Context已经被销毁后,却因为被引用导致GC回收失败。但是Application的Context对象却会随着当前进程而一直存在,所以使用Context是应该注意:
- 当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
- 不要让生命周期长于Activity的对象持有到Activity的引用。
- 尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。
参考博文:
https://www.jianshu.com/p/94e0f9ab3f1d
https://www.cnblogs.com/jingmengxintang/p/7889311.html
https://my.oschina.net/youranhongcha/blog/1807189
https://blog.csdn.net/qq_23547831/article/details/46481725
https://blog.csdn.net/guolin_blog/article/details/47028975
Android中的Context(一)的更多相关文章
- 绝对让你理解Android中的Context
这个问题是StackOverFlow上面一个热门的问题What is Context in Android? 整理这篇文章的目的是Context确实是一个非常抽象的东西.我们在项目中随手都会用到它,但 ...
- Android中,Context,什么是Context?
注:本文翻译自Context, What Context?,原文链接在这里,作者是Dave Smith.ps:译者链接http://blog.csdn.net/race604/article/deta ...
- Android中的Context
Context用来访问全局信息的接口,比如影城程序的资源.一些常用的组件都是继承自Context,目的就是方便的访问资源,比如Activity, Service.... 从Context访问本组件的资 ...
- Android中的Context详解
前言:本文是我读<Android内核剖析>第7章 后形成的读书笔记 ,在此向欲了解Android框架的书籍推荐此书. 大家好, 今天给大家介绍下我们在应用开发中最熟悉而陌生的朋友---- ...
- Android 中的 Context
主要的功能是加载和访问资源(Context通常用来获取APP资源,创建UI,获取系统Service服务,启动Activity,绑定Service,发送广播,获取APP信息等) 如何理解: 我们可以理解 ...
- android 中的Context(一)
context的功能如此强大,它是activity的父类. public abstract class Context { ... public abstract Object getSystemSe ...
- Android中的单例模式
定义: 单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 使用场景: 确保某一个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一 ...
- Android中JNI编程详解
前几天在参加腾讯模拟考的时候,腾讯出了一道关于JNI的题,具体如下: JNI本身是一个非常复杂的知识,但是其实对于腾讯的这道题而言,如果你懂JNI,那么你可能会觉得这道题非常简单,就相当于C语言中的h ...
- 【转】深入理解Android中的SharedPreferences
SharedPreferences作为Android中数据存储方式的一种,我们经常会用到,它适合用来保存那些少量的数据,特别是键值对数据,比如配置信息,登录信息等.不过要想做到正确使用SharedPr ...
随机推荐
- nginx运用
1.nginx的 命令 start nginx 这样,nginx 服务就启动了.打开任务管理器,查看 nginx.exe 进程,有二个进程会显示,占用系统资源,那是相当的少.然后再打开浏览器,输入 h ...
- 基于Java服务的前后端分离解决跨域问题
导语:解决跨域问题,前后端都增加相应的允许跨域的代码段即可. 一.后端增加允许跨域的代码,可以在具体controler层加,最好是在filter中添加,这样添加一次就够了,不用在每个controler ...
- Xgboost_sklearn代码Demo
Demo: 显示特征的重要程度:图形化展示: from numpy import loadtxt from xgboost import XGBClassifier from xgboost impo ...
- MapReduce编程模型简介和总结
MapReduce应用广泛的原因之一就是其易用性,提供了一个高度抽象化而变得非常简单的编程模型,它是在总结大量应用的共同特点的基础上抽象出来的分布式计算框架,在其编程模型中,任务可以被分解成相互独立的 ...
- TreeMap,HashMap,LinkedHashMap区别,很简单解释
TreeMap,HashMap,LinkedHashMap之间的区别和TreeSet,HashSet,LinkedHashSet之间的区别相似. TreeMap:内部排序. HashMap:无序. L ...
- 【转载】SSH协议及其应用
原文作者:阮一峰 链接: http://www.ruanyifeng.com/blog/2011/12/ssh_remote_login.html http://www.ruanyifeng.com/ ...
- linux下的nmap工具能干什么?
答:可以用来探测远程主机的操作系统类型,使用方法如下: nmap -A <ip address>
- win10 开机自启指定软件
开机自启 %programdata%\Microsoft\Windows\Start Menu\Programs\StartUp
- C# Winform ComBox三种赋值方式
https://www.cnblogs.com/ingstyle/p/4815303.html 第一种方法: DataTable dt = new DataTable(); dt.Columns.Ad ...
- Deep Learning--week1~week3
week1 一张图片,设像素为64*64, 颜色通道为红蓝绿三通道,则对应3个64*64实数矩阵 为了用向量表示这些矩阵,将这些矩阵的像素值展开为一个向量x作为算法的输入 从红色到绿色再到蓝色,依次按 ...