Android的Context是一个经常用到的概念,由于它是一个抽象类所以更多时候我们见到或使用到的是它的子类,比如getApplicationContext(),getBaseContext(),getContext()。中文一般翻译成“上下文”,结合汉语中对上下文的解释不难理解Android环境中对Context的定义和重要性。Android应用是由一个个组件组成的(各个Activity,Service,View等),想要合理的沟通和运行这些组件就需要Context来作为“桥梁”。这篇文章就来好好总结下Android中Context相关的知识点。
1.概念
首先来看官方对Context的解释:
Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.
这段话告诉我们这几点信息:
1.Context是应用环境的全局信息接口;
2.Context是抽象类不能直接使用,具体实现交给Android系统;
3.Context用来访问资源和类,启动activity,brocadcast和接受intent等。
下面先好好解释下这3点信息。
1.1 应用全局信息接口
上面提到Context是Android各个组件沟通桥梁,主要就是四大组件+资源。看下面一张图:
Activity类继承了ContextThemeWrapper,而Service继承了ContextWrapper,都间接继承了Context。ContextImpl是Context的具体实现,ContextWrapper是对ContextImpl的进一步包装(利用mBase对象,实际是一个Context对象)。ActivityThread类是Android应用框架的核心对象,它就在ContextImpl类中。有了这些认知我们可以更好的理解官方概念中的“应用全局信息接口”这句话。
1.2 Android系统实现具体Context
Android的具体Context其实有多种,getApplicationContext()方法得到的是一个APP全局的Context,在Fragment中getActivity()方法得到的又是一个Activity类型的Context,View中又通常采用getContext()来获取Context。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Context context0 = getApplicationContext();
Context context1 = getApplication();
Context context2 = getBaseContext();
Context context3 = this;
Context context4 = new ContextWrapper(this);
Log.d(TAG,">>>>>>> getApplicationContext = " + context0);
Log.d(TAG,">>>>>>> getApplication = " + context1);
Log.d(TAG,">>>>>>> getBaseContext = " + context2);
Log.d(TAG,">>>>>>> activity.this = " + context3);
Log.d(TAG,">>>>>>> new ContextWrapper(this) = " + context4);
}
}
//打印
D/MainActivity: >>>>>>> getApplicationContext = android.app.Application@5ec6462
D/MainActivity: >>>>>>> getApplication = android.app.Application@5ec6462
D/MainActivity: >>>>>>> getBaseContext = androidx.appcompat.view.ContextThemeWrapper@208c9f3
D/MainActivity: >>>>>>> activity.this = com.zsf.androidadvanceddecode.MainActivity@22bd968
D/MainActivity: >>>>>>> new ContextWrapper(this) = android.content.ContextWrapper@3836b0
1.3 访问资源和类
这个点主要可以看ContextWrapper类,在这个类中保存了Theme资源和Resource资源。很多系统级服务都需要使用Context来调用,比如WiFi:
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
2.Application Context介绍
还是上面那张图,ContextImpl是Context抽象类的具体实现,Application类继承与ContextWrapper,Application又是一个APP的唯一实例(APP启动,Application创建,且在整个APP生命周期内都只有这一个Application实例),我们使用getApplicationContext()来获取一个全局性的Context。思考它是如何创建出来的?
上面提到APP核心框架类ActivityThread,它作为应用进程的主线程管理类,负责启动activity,广播和activity有关请求操作。
//frameworks/base/core/java/android/app/ActivityThread.java
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
int procState, Bundle state, PersistableBundle persistentState,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.referrer = referrer;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profilerInfo = profilerInfo;
r.overrideConfig = overrideConfig;
updatePendingConfiguration(curConfig);
//发送Handler消息启动Activity
sendMessage(H.LAUNCH_ACTIVITY, r);
}
H是ActivityThread的内部类:
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
//...省略
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
//获的LoadedApk对象
r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
//继续点进去该方法
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
break;
//...省略
}
}
//...省略
}
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
//...
Activity a = performLaunchActivity(r, customIntent);
//...
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//...
//总算看到了有关Application的
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
//...
}
///frameworks/base/core/java/android/app/LoadedApk.java
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
//第一次启动APP,mApplication = null
if (mApplication != null) {
return mApplication;
}
//...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
//创建Application
app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
//将Application类型的对象app赋值给变量mOuterContext,它是一个ContextImpl对象
appContext.setOuterContext(app);
//...
//将app赋值给成员变量mApplication,它是Application类型的对象
mApplication = app;
//...
}
到这里我们已经离终点不远了,接下来我们继续看newApplication(cl, appClass, appContext)是如何创建Application的。
//frameworks/base/core/java/android/app/Instrumentation.java
static public Application newApplication(Class<?> clazz, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Application app = (Application) clazz.newInstance();
app.attach(context);
return app;
}
//frameworks/base/core/java/android/app/Application.java
final void attach(Context context) {
//ContextWrapper中实现该方法
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
//frameworks/base/core/java/android/content/ContextWrapper.java
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
//base是ContextImpl,将它赋值给成员变量mBase
mBase = base;
}
ContextWrapper的成员变量mBase获取了ContextImpl,ComtextImpl又是Context的具体实现,Application又是继承了ContextWrappter。到这里一个APP成功创建了一个Application Context。
3.Activity Context
下面我们再看下Activity中Context是怎么来的。有了前面Application Context的经历,下面将仅记录关键代码。
//frameworks/base/core/java/android/app/ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//...
//创建Activity的ContextImpl
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
//...
} catch (Exception e) {
//...
}
//...
if (activity != null) {
//...
//赋值给ContextImpl的成员变量mOuterContext
appContext.setOuterContext(activity);
//传入appContext
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);
}
//...
}
下面的分析参考Application Context的过程即可。
4. 其他有关Context的认识
1.实际开发中getApplication()方法一般用在Activity/Service中,而getApplicationContext()一般用在广播BroadcastReceiver中。上面的打印我们也可以看到这两个其实代表了同一个对象(对象地址一样)。
2.Dialog开发中,一般传递的是Activity的Context,因为Android要求Dialog不能独立存在,必须依附与Activity界面。
3.一个App进程Context数量=Activity数量+Service数量+1(Application);