Context基础知识

Context类型

Context是我们开发中通常说的上下文或者环境。

1
MainActivity activity =  new MainActivity();

很多人应该和我有过相同的疑问,就是Activity为什么不能向上面那样直接new一个实例,而非要通过context.startActivity。Android应用模型是基于组件的应用设计模式,组件的运行要有一个完整的Android工程环境。 Activity Service 等组件都需要这么一个上下文环境(即Context)才可以正常工作,采用new创建实例的话并不能提供这个Context。

Context的继承结构:

Context

Context有两个直接的子类ContextImpl和ContextWrapper。ContextWrapper有三个子类Application子类: Application、Service、ContextThemeWrapper。Activity则继承自ContextThemeWrapper。自此,Application、Service、Activity都出来了。实际上Context一共有三种类型、分别是Application、Activity和Service。

ContextImpl是Context功能的实现类、而ContextWrapper则是Context功能的封装类。通常情况下Context是可以共用的,如Toast、操作sp、数据库、启动service、发送广播等。但是出于安全方面的原因,启动Activity和弹Diolog必须使用Activity的Context。

给Activity设置Flag标记位为 FLAG_ACTIVITY_NEW_TASK 时可以使用ApplicationContext的Context。

系统Dialog可以不使用Activity的Context。

Application和Service中layout inflate也是合法的,但是会使用系统默认的主题样式。

凡是跟UI相关的,都应该使用Activity做为Context来处理。其他情况优先使用Applicationt的Context,可有效避免有关的内存泄漏。

ContextWrapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}

protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
public Context getBaseContext() {
return mBase;
}

@Override
public AssetManager getAssets() {
return mBase.getAssets();
}

@Override
public Resources getResources() {
return mBase.getResources();
}

@Override
public PackageManager getPackageManager() {
return mBase.getPackageManager();
}

@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}

@Override
public Looper getMainLooper() {
return mBase.getMainLooper();
}

@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
}

......
}

ContextWrapper是Context的包装类,实际上调用的是mBase。也就是attachBaseContext(Context base)中传入的Context.attachBaseContext()方法是由系统调用的,会把ContextImpl对象作为参数传入。因此ContextImpl是Context的实现类,ContextWrapper调用的是ContextImpl的相关方法。

Context数量

很多面试题中会问:一个应用程序中到底有多少个Context呢?其实根据Context类型我们就已经可以知道Context一共有Application、Activity和Service三种类型,因此一个应用程序中Context数量的计算公式可以这样写:

Context数量 = Activity数量 + Service数量 + Application数量

通常情况下Application的数量为1,因为一个应用程序中可以有多个Activity和多个Service,但是只能有一个Application。

但是对于多进程的情况下呢?此时Application会n次(n=进程个数)重新创建,此时Application数量是应该是1还是n?

关于这个问题还可以延伸出更多的问题:比如

  • 不同进程里的Activity获取getApplication对象是否一致
  • 针对多进程如何优化自定义的Application