1. 概述
官方的解释:
有关应用程序环境的全局信息的接口。 这是一个抽象类,实现类是 ContextImpl
。 它允许访问特定于应用程序的资源和类,以及调用应用程序级操作,eg:
- 启动Activity (startActivity)
- 启动服务 (startService)
- 发送广播 (sendBroadcast), 注册广播接收者 (registerReceiver)
- 获取ContentResolver (getContentResolver)
- 获取类加载器 (getClassLoader)
- 打开或创建数据库 (openOrCreateDatabase)
- 获取资源 (getResources)
Android Context本身是一个抽象类。ContextImpl, Activity, Service, Application这些都是Context的直接或间接子类, 下面通过看看这些类的关系,如下:
- Application/Activity/Service通过
attach()
调用父类ContextWrapper
的attachBaseContext()
, 从而设置父类成员变量mBase
为ContextImpl
对象; -
ContextWrapper
的核心工作都是交给mBase
(即ContextImpl
)来完成;
- Application: 四大组件属于某一Application, 获取所在Application:
- Activity/Service: 是通过调用其方法getApplication(),可主动获取当前所在mApplication;
mApplication是由LoadedApk.makeApplication()过程所初始化的; - Receiver: 是通过其方法onReceive()的第一个参数指向通当前所在Application,也就是只有接收到广播的时候才能拿到当前的Application对象;
- provider: 目前没有提供直接获取当前所在Application的方法, 但可通过getContext()可以获取当前的ContextImpl.
1.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.
*/
public abstract class Context{
public abstract AssetManager getAssets();
public abstract Looper getMainLooper();
public abstract void startActivity(...);
...
}
1.2 ContextWrapper
/**
* Proxying implementation of Context that simply delegates all of its calls to
* another Context. Can be subclassed to modify behavior without changing
* the original Context.
*/
public class ContextWrapper extends Context {
@UnsupportedAppUsage
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
/**
* Set the base context for this ContextWrapper. All calls will then be
* delegated to the base context. Throws
* IllegalStateException if a base context has already been set.
*
* @param base The new base context for this wrapper.
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
/**
* @return the base context as set by the constructor or setBaseContext
*/
public Context getBaseContext() {
return mBase;
}
...
}
1.3 ContextThemeWrapper
/**
* A context wrapper that allows you to modify or replace the theme of the wrapped context.
*/
public class ContextThemeWrapper extends ContextWrapper {
private int mThemeResource;
private Resources.Theme mTheme;
private LayoutInflater mInflater;
private Configuration mOverrideConfiguration;
private Resources mResources;
/**
* Creates a new context wrapper with no theme and no base context.
* <p class="note">
* <strong>Note:</strong> A base context <strong>must</strong> be attached
* using {@link #attachBaseContext(Context)} before calling any other
* method on the newly constructed context wrapper.
*/
public ContextThemeWrapper() {
super(null);
}
/**
* Creates a new context wrapper with the specified theme.
* <p>
* The specified theme will be applied on top of the base context's theme.
* Any attributes not explicitly defined in the theme identified by
* <var>themeResId</var> will retain their original values.
*
* @param base the base context
* @param themeResId the resource ID of the theme to be applied on top of
* the base context's theme
*/
public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
super(base);
mThemeResource = themeResId;
}
/**
* Creates a new context wrapper with the specified theme.
* <p>
* Unlike {@link #ContextThemeWrapper(Context, int)}, the theme passed to
* this constructor will completely replace the base context's theme.
*
* @param base the base context
* @param theme the theme against which resources should be inflated
*/
public ContextThemeWrapper(Context base, Resources.Theme theme) {
super(base);
mTheme = theme;
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
}
...
}
1.4 Application
/**
* Base class for maintaining global application state. ...
*/
public class Application extends ContextWrapper implements ComponentCallbacks2 {
...
}
1.5 Service
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
...
}
2. 组件初始化 – Activity
[-> ActivityThread.java]
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
//step 1: 创建LoadedApk对象
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
... //component初始化过程
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
//step 2: 创建Activity对象
Activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
...
//step 3: 创建Application对象
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
//step 4: 创建ContextImpl对象
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
//step5: 将Application/ContextImpl都attach到Activity对象 [见小节4.1]
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);
...
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
//step 6: 执行回调onCreate
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
activity.performStart(); //执行回调onStart
r.stopped = false;
}
if (!r.activity.mFinished) {
//执行回调onRestoreInstanceState
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
...
r.paused = true;
mActivities.put(r.token, r);
}
return activity;
}
startActivity的过程最终会在目标进程执行performLaunchActivity()方法, 该方法主要功能:
- 创建对象LoadedApk;
- 创建对象Activity;
- 创建对象Application;
- 创建对象ContextImpl;
- Application/ContextImpl都attach到Activity对象;
- 执行onCreate()等回调;
3. 创建ContextImpl
创建ContextImpl的方式有多种, 不同的组件初始化调用不同的方法,如下:
- Activity: 调用createBaseContextForActivity初始化;
- Service/Application: 调用createAppContext初始化;
- Provider: 调用createPackageContext初始化;
- BroadcastReceiver: 直接从Application.getBaseContext()来获取ContextImpl对象;
3.1 createBaseContextForActivity
[-> ActivityThread.java]
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
int displayId = Display.DEFAULT_DISPLAY;
try {
displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
} catch (RemoteException e) {
}
//创建ContextImpl对象
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, displayId, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
...
return baseContext;
}
[-> ContextImpl.java]
static ContextImpl createActivityContext(ActivityThread mainThread, LoadedApk packageInfo, int displayId, Configuration overrideConfiguration) {
return new ContextImpl(null, mainThread, packageInfo,
null, null, false,
null, overrideConfiguration, displayId);
}
3.2 createAppContext
[-> ContextImpl.java]
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
return new ContextImpl(null, mainThread, packageInfo,
null, null, false,
null, null, Display.INVALID_DISPLAY);
}
3.4 ContextImpl初始化
[-> ContextImpl.java]
class ContextImpl extends Context {
final ActivityThread mMainThread;
final LoadedApk mPackageInfo;
private final IBinder mActivityToken;
private final String mBasePackageName;
private Context mOuterContext;
//缓存Binder服务
final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();
private ContextImpl(ContextImpl container, ActivityThread mainThread, LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted, Display display, Configuration overrideConfiguration, int createDisplayWithId) {
mOuterContext = this; //ContextImpl对象
mMainThread = mainThread; // ActivityThread赋值
mPackageInfo = packageInfo; // LoadedApk赋值
mBasePackageName = packageInfo.mPackageName; //mBasePackageName等于“android”
...
}
}
4. Context attach过程
[-> Activity.java]
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) {
attachBaseContext(context); //调用父类方法设置mBase.
mUiThread = Thread.currentThread();
mMainThread = aThread;
mApplication = application;
mIntent = intent;
mComponent = intent.getComponent();
mActivityInfo = info;
...
}
将新创建的 ContextImpl
赋值到父类 ContextWrapper.mBase
变量.
5. 总结
5.1 组件初始化
下面用一幅图来看看核心组件的初始化过程会创建哪些对象:
每个Apk都对应唯一的application对象和LoadedApk对象, 当Apk中任意组件的创建过程中, 当其所对应的的LoadedApk和Application没有初始化则会创建, 且只会创建一次.
另外大家会注意到唯有Provider
在初始化过程并不会去创建所相应的Application对象.也就意味着当有多个Apk运行在同一个进程的情况下, 第二个apk通过Provider初始化过程再调用getContext().getApplicationContext()
返回的并非Application
对象, 而是NULL
. 这里要注意会抛出空指针异常.
5.2 Context attach过程
- 调用attachBaseContext()将新创建ContextImpl赋值到父类 ContextWrapper.mBase变量;
- 可通过getBaseContext()获取该ContextImpl;
- 调用attachBaseContext() 将新创建ContextImpl赋值到父类ContextWrapper.mBase变量;
- 可通过getBaseContext()获取该ContextImpl;
- 可通过getApplication()获取其所在的Application对象;
- 调用attachInfo()将新创建ContextImpl保存到ContentProvider.mContext变量;
- 可通过getContext()获取该ContextImpl;
- 在onCreate过程通过参数将ReceiverRestrictedContext传递过去的.
- 可通过getApplicationContext()获取Application;
5.3 Context使用场景
startActivity
操作
- 当为Activity Context则可直接使用;
- 当为其他Context, 则必须带上FLAG_ACTIVITY_NEW_TASK flags才能使用;
- 另外UI相关要Activity中使用.
5.4 getApplicationContext
Tips: 如果对于Application理解不够深刻, 建议getApplicationContext()方法谨慎使用, 做好是否为空的判定,防止出现空指针异常.
绝大多数情况下, getApplication()
和getApplicationContext()
这两个方法完全一致, 返回值也相同; 那么两者到底有什么区别呢? 真正理解这个问题的人非常少. 接下来彻底地回答下这个问题:
getApplicationContext()
这个的存在是Android历史原因. 我们都知道getApplication()
只存在于Activity和Service对象; 那么对于BroadcastReceiver和ContentProvider却无法获取Application, 这时就需要一个能在Context上下文直接使用的方法, 那便是getApplicationContext().
两者对比:
- 对于
Activity/Service
来说,getApplication()
和getApplicationContext()
的返回值完全相同; 除非厂商修改过接口; -
BroadcastReceiver
在onReceive
的过程, 能使用getBaseContext().getApplicationContext
获取所在Application
, 而无法使用getApplication
; -
ContentProvider
能使用getContext().getApplicationContext()
获取所在Application
. 绝大多数情况下没有问题, 但是有可能会出现空指针的问题, 情况如下:
当同一个进程有多个 apk
的情况下, 对于第二个 apk
是由 provider
方式拉起的, 前面介绍过 provider
创建过程并不会初始化所在 application
, 此时执行 getContext().getApplicationContext()
返回的结果便是 NULL
. 所以对于这种情况要做好判空.
参考链接