前言
本文主要讲解Context这一套的东西,包括Context相关的一套所涉及的设计模式,只有当理解设计模式之后我们才能更好的理解Context。然后希望大家看完本篇文章之后,对Context能够了解得更多,平时不仅仅限于只会使用这一套东西。
内容
Activity、Service和Application的流程图(下面暂时只提及Activity、可类推...):
Context到Activity Context到服务 Context到Application
这一套流程图很多人都已经知道了,Context包含着很多的抽象方法,包括最常见的startActivity()、getApplicationContext()、getColor()......
。因为活动继承自Context,而Context里面的抽象方法已经被实现过了,那么这些方法是我们在活动中都可以调用到的方法,是不是已经有些感觉了?就是对“Contex->环境”的这个概念的理解,身处Context这个“大环境中”,我就可以实现一些功能:进行资源访问、访问其它组件和调用底层API等等。
活动继承自Context,但是活动一个Contxet的抽象方法都不用实现,因为Context的抽象方法全被ContextWrapper覆写了,但是Context真正的实现类又是ContextImpl。那么这之中的奥妙之处在哪里呢?我们先看了一个设计模式——装饰者模式。
设计模式
这里参考《Android源码设计模式》上的一个例子,很通俗易懂。
一个人需要穿衣服,而衣服就是对他人的修饰。
/**
* 一个人的抽象
*/
public abstract class Person {
/**
* 穿衣服的抽象
*/
public abstract void dressed();
}
/**
* 一个人的具体实现
*/
public class PersonImpl extends Person {
/**
* 穿衣服的具体实现
*/
@Override
public void dressed() {
System.out.println("穿上内衣内裤");
}
}
/**
* 人的穿着的抽象
*/
public abstract class PersonCloth extends Person {
private Person mPerson;
public PersonCloth(Person person) {
mPerson = person;
}
@Override
public void dressed() {
mPerson.dressed();
System.out.println("穿一件上衣、穿一条裤子");
}
}
/**
* 很贵的穿着装饰
*/
public class ExpensiveCloth extends PersonCloth {
public ExpensiveCloth(Person person) {
super(person);
}
@Override
public void dressed() {
super.dressed();
System.out.println("穿一件2万的外套、一双5千的鞋子");
}
}
/**
* 实现
*/
public static void main(String[] args) {
Person person = new PersonImpl();
PersonCloth cloth = new ExpensiveCloth(person);
cloth.dressed();
}
分析: 人和它的子类很容易理解,关键在于它的装饰者的理解。装饰者需要拿到一个“人”,需要它装饰的对象,然后需要该对象进行装饰,也就是扩展其它的功能。为了代码的可扩展性,这里的装饰者也是具有继承的层级分布。
显然,继承自Context之后的类都可以理解成它的装饰者,但是也不尽是装饰者,比如Activity之类的,显然Activity是利用Context这一套环境来实现自己的功能。
装饰模式的好处:
- 很符合开闭原则,通过扩展代码来修改、增加功能, 利于后期项目迭代。
- 灵活性高,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。
Context的实现过程
这里就不再讲解Context的实现类了,ContextImpl就是实现了Context的所有抽象方法,就是一个资源的获取、管理和组件之间的管理等等。我们找Context是何时被初始化和赋值在ContextWrapper中的。
Activity的启动是在ActivityThread中,找到关键方法performLaunchActivity()
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//配置信息
......
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
//这里拿到的是成员对象Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
//连接Activity
......
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
......
return activity;
}
咱们先看ContextImpl对象是如何创建的:
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
......
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
//在调试状态下,如果有辅助显示器,则在辅助显示器上显示
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
String pkgName = SystemProperties.get("debug.second-display.pkg");
if (pkgName != null && !pkgName.isEmpty()
&& r.packageInfo.mPackageName.contains(pkgName)) {
for (int id : dm.getDisplayIds()) {
if (id != Display.DEFAULT_DISPLAY) {
Display display =
dm.getCompatibleDisplay(id, appContext.getResources());
appContext = (ContextImpl) appContext.createDisplayContext(display);
break;
}
}
}
return appContext;
}
在createActivityContext()
方法中还是采用直接new的方法来获取实例。这也是为什么Context能够作为Activity、Service、Application的父类了,ContextImpl没有区分当前是哪一个Context子类,因此是一个通用的“环境”。
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
activityToken, null, 0, classLoader);
我们回到performLaunchActivity()
方法中,该方法中还有一句makeApplication()
,就是把成员对象Application拿到,因此它只有一个,那么一个应用中的Context实例对象应该有 “活动数+服务数+1”个。
接下来再去activity.attach()
中看appContext
是如何被设置的,进去之后就有一句attachBaseContext(context);
,再次点击跳转,调用super.attachBaseContext(newBase);
,回到ContextWrapper类中,执行
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
到这儿就设置完成了。
此处有一个注意点,getBaseContext()
和getApplicationContext()
的区别,想必现在就很清楚了,为什么推荐使用getApplicationContext()
呢?因为这个方法使用的是ActivityThread中的成员对象mApplication
的Context引用,整个应用只会有一个Application对象,所以怎么用都不会存在内存泄漏,而getBaseContext()
则是拿到当前活动或者服务的Context应用,因此使用不当,导致当前活动或服务应用被持有而无法释放,内存泄漏。
总结
我是直接在ActivityThread中找到Activity的启动方法的,但是在Activity实例之前是有一大波逻辑分析的,感兴趣的可以看这篇文章
Context还是很容易理解的,光听“环境”这个词语很很懵,但是看一看源码就一目了然了嘛~
笔者水平有限,有写得不好的地方,请大胆指出~
请支持原创哦~
网友评论