想说的
不久前看了郭霖老师的关于Context的博客写的非常好,感觉很受用,为了加深自己的理解,记录下这篇博客。
关于Context
Context直接翻译就是上下文的意思,Android和Java程序一个很大的不同就是你不能随意在你想要的地方new一个对象,因为有上下文的限制,对于上下文我是这么理解的:
依上下文的字面来说:
就是一个对象或者是组件不能够凭空出现,必须有一个线索,而这个线索就是上下文,让这个对象或者是组件知道自己的上级和下级分别是什么,从而确定自己是否应该在这儿。
依理解后的意思来说:
就是我们的一些组件和对象不能够凭空运行,他需要一个环境来运行,而这里所指的环境就是我们的Context,因为实际上我们经常使用的Activity,Service本身就是一个Context对象,所以它们本身就是一个环境,所以,我们经常会使用到this关键字来获取这个运行环境。
Context的类型
context的关系以及继承图如图所示:(使用的郭霖老师的图)
从图中可以看出Context第一次分层是自己的封装类(ContextWrapper)和自己的实现类(ContextImpl),而它的封装类又有三个子类Application,Service,ContextThemeWrapper,而ContextThemeWrapper这个类又有一个子类Activity,这里我们可以得出这么个结论:
我们经常使用的Application,Service,Activity都是一个Context对象,当然receiver并不是。
同时从这幅图我们还可以看出,Context的封装类和实现类是分开的,也就是说,光有封装类是不行的,不能够使用Context的众多的方法,那么这个封装类和实现类是怎么联系起来的呢?这就是需要深入源码去查看了:
public class ContextWrapper extends Context {
Context mBase;
/**
* Set the base context for this ContextWrapper. All calls will then be
* delegated to the base context. Throws
* IllegalStateException if a base context has already been set.
*
* @param base The new base context for this wrapper.
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
/**
* @return the base context as set by the constructor or setBaseContext
*/
public Context getBaseContext() {
return mBase;
}
@Override
public AssetManager getAssets() {
return mBase.getAssets();
}
@Override
public Resources getResources() {
return mBase.getResources();
}
.....
可以看到在这个封装类中几乎所以的操作都是通过一个mBase来进行的,所以我们的重点就是看这个mBase是从何而来,看这个函数:
attachBaseContext(),里面传入了一个base参数给mBase,而这个Context类型的base正是我们的Context实现类,那么这个问题有能够解决了,Context的封装类和实现类确实的联系起来了!那么那上面的图也能够很清晰的理解了。
关于Application的使用
Application我们都用的很多,比如,如果你的Android应用中老是缺少上下文对象可以使用,那么就可以改写一下你的Application,并且获取到Application的上下文,因为Application是存在于Android应用的整个生命周期的,所以你完全可以使用,但是这其中所带来的性能问题,我目前不清楚,以后回顾这篇博客时希望能已经解决了。
接下来我们来看一下Application中的方法执行顺序:
从这幅图我们可以得到一些启示,虽然Application首先是执行它的构造方法,但是我们对Application的一些涉及到Context的操作却是不能够在构造方法中进行的,为什么?看图片一目了然,因为对于Context的操作,我们若是仅仅有Context这个对象是不够的,而在构造方法中我们确实是仅仅只有Context对象的,而将封装类和实现类进行联系起来还是在构造方法的下一步,使用我们刚才已经介绍过的方法:attchBaseContext(),用这个方法系统将这个实现类导入之后才可以用Context的一系列方法来进行丰富的操作。之后就执行了onCreate()方法,大部分的初始化操作都是在这个onCreate()方法中进行的,在这个方法中,可以肯定的是Context的封装类和实现类已经进行了联系,可以不用顾忌的使用Context的一系列操作来进行初始化操作。但是,如果确实是有那么一个需求需要在很早的时候就使用Context的一系列操作来进行初始化或者是一些操作,那么最极限的早就是在attchBaseContext()中进行,其实从这幅图可以看出,我个人是不推荐在这其中进行初始化操作的,因为在这个函数中,在执行中没有办法确定你的初始化操作是否在系统将实现类导入到封装类之前,万一是在导入实现类之前进行了初始化,那么肯定是会报错的,当然我估计大部分情况是在导入之后进行的初始化操作,但是万一呢,这个可能性没有办法去估计,说穿了就是不严谨,所以可以得出这么个结论:
在Application中的一系列初始化操作都固定写在onCreate()方法中,在这个方法中可以百分百确定我们对Context的使用是没有问题的。
ps1:
上文提到的可以随时取用Application的Context对我们缺少Context的地方进行补充的方法的代码如下:
在这幅图中,可以看到我对Context的获取是在onCreate()去进行获取的,这正好符合我自己所说的事实。
ps2:
这是我第一次使用MarkDown来进行博客的撰写,我觉得这个真的很好用,美观,当然我现在对这部分的使用并不熟练,不过以后应该会一直使用MarkDown了。