盒子
盒子

Context解析

Context作为Activity,Service,Application的上下文环境,提供了许多方法,其继承结构如下图:

以下图片来自互联网:
Context继承树

由图可知,其有两个直接子类,分别是ContentWrapper和ContentImpl,其中ContentWrapper实现了对Context方法的封装:

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
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();
}
......
}

如上所示ContextWrapper对Context的方法进行封装,并由attachBaseContext方法提供过来的Context实例作为Activity,Service和Application的调用渠道

而attachBaseContext方法是在什么时候调用的呢?

从源码下手,以Activity为例,在创建的Activity的重写方法中可以找到attachBaseContext方法,而当前Activity一般都继承AppCompatActivity,顺着继承关系一路找下去,会在Activity类的atach方法中调用了attachBaseContext

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
49
50
51
52
53
54
55
56
57
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
mUiThread = Thread.currentThread();
mMainThread = aThread;
mInstrumentation = instr;
mToken = token;
mIdent = ident;
mApplication = application;
mIntent = intent;
mReferrer = referrer;
mComponent = intent.getComponent();
mActivityInfo = info;
mTitle = title;
mParent = parent;
mEmbeddedID = id;
mLastNonConfigurationInstances = lastNonConfigurationInstances;
if (voiceInteractor != null) {
if (lastNonConfigurationInstances != null) {
mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
} else {
mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
Looper.myLooper());
}
}
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;

mWindow.setColorMode(info.colorMode);

setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled());
enableAutofillCompatibilityIfNeeded();
}

而attach又在哪调用呢?这有点难找,需要对activity的启动过程进行分析,经过对Activity启动过程的代码追踪可知,attach在ActivityThread类中的performLaunchActivity方法中调用:

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
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
....
appContext.setOuterContext(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);

if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
checkAndBlockForNetworkAccess();
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
....
}

由以上代码的以下代码可以看出attach时在onCreate方法前调用的

1
2
3
4
5
6
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);
......
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);

由此也可得出一个结论,不能在构造方法中获取Context,但可以在onCreate中获取,而最好的获取方法时在重写attachBaseContext方法在super.attachBaseContext后获取

然后回到前面说的ContentWrapper实现了对Context方法的封装,其中mBase就是每个创建的activity通过attachBaseContext方法提供过去的context,从此就可直接通过传过去的封装了方法后的context来获取Context中的方法

ContentImpl类是一个Context类的实现类,其实现了Context的方法

曾经有一个问题就是android是否可以直接通过实例化一个Activity类来创建一个Activity,这不会出错,但这创建的仅仅只是一个实例化对象,并没有Activity的整套启动流程,既没有Context上下文环境也没有什么生命周期可言,所以通过该方法实例化出来的Activity不算是一个真正的activity

以下图片来自无互联网:
方法适应图

常用getContext,this,getApplicationContext和getApplication

getContext和this都是获取当前组件的context,而getAppliction和getApplicationContext都是获取Application的Context(在一些需要伴随app整个生命周期的组件中要是有application的Context而不要使用当前的Context,否则容易导致内存泄漏),要注意的时getApplication只能在Activity和Service中使用,对于BroadcastReceiver想要获取Application的Context就只能使用context.getApplicationContext了:

1
2
3
4
@Override
public void onReceive(Context context, Intent intent) {
context.getApplicationContext();
}
支持一下
扫一扫,支持Grooter
  • 微信扫一扫
  • 支付宝扫一扫