oynix

于无声处听惊雷,于无色处见繁花

Android Context

曾经被问过一个问题:Android系统中的Context有几种?当时没怎么说清楚,现在再来聊聊这个问题。

Context是个抽象类,它有两个直接子类:ContextImpl和ContextWrapper。ContextImpl实现了Context中的所有抽象方法,ContextWrapper也实现了Context的抽象方法,但它的实现方式是通过调用其持有的ContextImpl实例mBase的对应的方法,所以它叫做Wrapper,可以理解成ContextImpl的包装类。

1
2
3
Context
|--ContextImpl
|--ContextWrapper

而Application、Service都是ContextWrapper的直接子类。此外,ContextWrapper还有其他直接子类,如ContextThemeWrapper、ReceiverRestrictedContext,而Activity则是ContextThemeWrapper的直接子类,因为它需要主题。ReceiverRestrictedContext则是给BroadcastReceiver使用的,后面说。

1
2
3
4
5
6
7
8
Context
|--ContextImpl
|--ContextWrapper
|--Appliction
|--Service
|--ReceiverRestrictedContext
|--ContextThemeWrapper
|--Activity

这几个类的实例都是在ActivityThread中创建,其中的Context也是在这里面赋值的,分别来看一看

Activity

创建Activity时,最终会走到ActivityThread.performLauncherActivity方法里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ActivityThread.java 省略了部分代码
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 创建ContextImpl
ContextImpl appContext = createBaseContextForActivity(r);
// 创建Activity
Activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
// 创建Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
// 调用Activity.attach
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, r.assistToken);
// 调用Activity.onCreate
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
}

private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
return appContext;
}

可以看到,创建一个Activity的过程主要有3步:一是通过反射创建Activity实例,二是准备相关的参数,其中包含ContextImpl实例和Application实例,接着是attach,在这个方法里初始化Activity的成员变量,其中有一个Application类型的变量mApplication,传入的Application是为了初始化这个变量。

Service

创建Service最终会走到ActivityThread.handleCreateService方法里

1
2
3
4
5
6
7
8
9
10
11
12
13
// ActivityThread.java 省略了部分代码
private void handleCreateService(CreateServiceData data) {
// 创建ContextImpl
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
// 创建Application
Application app = packageInfo.makeApplication(false, mInstrumentation);
// 创建Service
Service service = packageInfo.getAppFactory().instantiateService(cl, data.info.name, data.intent);
// 调用Service.attach
service.attach(context, this, data.info.name, data.token, app, ActivityManager.getService());
// 调用Service.onCreate
service.onCreate();
}

和Activity类似,不再赘述。

BroadcastReceiver

创建BroadcastReceiver最终会走到ActivityThread.handleReceiver方法里

1
2
3
4
5
6
7
8
9
10
11
// ActivityThread.java 省略了部分代码
private void handleReceiver(ReceiverData data) {
// 创建Application
Application app = packageInfo.makeApplication(false, mInstrumentation);
// 创建ContextImpl
ContextImpl context = (ContextImpl) app.getBaseContext();
// 创建BroadcastReceiver
BroadcastReceiver receiver = packageInfo.getAppFactory().instantiateReceiver(cl, data.info.name, data.intent);
// 调用BroadcastReceiver.onReceive
receiver.onReceive(context.getReceiverRestrictedContext(), data.intent);
}

可以看到,BroadcastReceiver没有直接使用创建的ContextImpl,而是通过context.getReceiverRestrictedContext()获取了ReceiverRestrictedContext,上面提过,它是ContextWrapper的直接子类,重写了其中的registerReceiver和bindService方法,方法里直接抛出异常,所以用BroadcastReceiver中的Context无法创建广播和服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ContextImpl.java 只展示关键代码
class ReceiverRestrictedContext extends ContextWrapper {
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) {
if (receiver == null) {
// Allow retrieving current sticky broadcast; this is safe since we
// aren't actually registering a receiver.
return super.registerReceiver(null, filter, broadcastPermission, scheduler);
} else {
throw new ReceiverCallNotAllowedException("BroadcastReceiver components are not allowed to register to receive intents");
}
}

@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
throw new ReceiverCallNotAllowedException("BroadcastReceiver components are not allowed to bind to services");
}
}

Application

在创建上面3个的时候可以看到,Application都是通过packageInfo.makeApplication来创建的,packageInfo是个类型为LoadedApk类型的实例,具体看一看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// LoadedApk.java 只展示了关键代码
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
mApplication = app;
// 调用Application的onCreate方法
instrumentation.callApplicationOnCreate(app);
}

// Instrumentation.java
public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Application app = getFactory(context.getPackageName()).instantiateApplication(cl, className);
app.attach(context);
return app;
}

先创建了ContextImpl的实例,然后在Instrumentation中通过反射创建了Application实例,接着将上一步创建的ContextImpl实例通过attach方法传入,最后调用Application.onCreate方法

ContentProvider

四大组件说了三个,再说说最后一个ContentProvicer,虽然不是Context的子类,但也和Context相关。创建Provider在ActivityThread.installProvider方法中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ContextImpl.java 只展示关键代码
private ContentProviderHolder installProvider(Context context, ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) {
Context c = null;
ApplicationInfo ai = info.applicationInfo;
if (context.getPackageName().equals(ai.packageName)) {
c = context;
} else if (mInitialApplication != null && mInitialApplication.getPackageName().equals(ai.packageName)) {
c = mInitialApplication;
} else {
try {
c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE);
} catch (PackageManager.NameNotFoundException e) { // Ignore }
}
// 创建ContentProvider
ContentProvider localProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);
localProvider.attachInfo(c, info);
}

视情况而定,ContextImpl的来源有多种,但最后都是通过attachInfo方法传入ContentProvider。

总结

回到最初的问题,Context有几种呢,就普遍性而言有3种:Application、Activity和Service,如果严谨一点,算上ReceiverRestrictedContext的话,那么就是4种。

------------- (完) -------------
  • 本文作者: oynix
  • 本文链接: https://oynix.com/2021/09/af6e257818b9/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

欢迎关注我的其它发布渠道