Context
本身是一个抽象类,主要实现类为 ContextImpl
,另外有子类 ContextWrapper
和 ContextThemeWrapper
,这两个子类都是 Context
的代理类,主要区别是 ContextThemeWrapper
有自己的主题资源。它们继承关系如下::
一、Context有什么用
先来看下官方对 Context
的定义:Context
是一个由Android系统提供实现的抽象类,它是一个接口,允许你访问和操作应用程序环境的全局信息。这些信息和操作包括:
- 访问特定应用程序的资源和类,例如,你可以通过
Context
来访问应用程序的布局资源、字符串资源等。 - 执行应用程序级别的操作,例如启动活动、发送和接收广播等。
因此,无论你是在哪个部分的代码中,只要你有Context
,你就可以执行这些操作,访问这些资源。这就是为什么Context
在Android开发中如此重要,它是连接各个组件,使其能够正常工作的桥梁。
再来看一下它提供了什么接口,下面列举一些主要的:
/**
* 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 void startActivity(@RequiresPermission Intent intent);
public abstract void sendBroadcast(@RequiresPermission Intent intent);
public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
IntentFilter filter);
public abstract void unregisterReceiver(BroadcastReceiver receiver);
public abstract ComponentName startService(Intent service);
public abstract boolean stopService(Intent service);
public abstract boolean bindService(@RequiresPermission @NonNull Intent service,
@NonNull ServiceConnection conn, int flags);
public abstract void unbindService(@NonNull ServiceConnection conn);
public abstract ContentResolver getContentResolver();
// 获取系统/应用资源
public abstract AssetManager getAssets();
public abstract Resources getResources();
public abstract PackageManager getPackageManager();
public abstract Context getApplicationContext();
public abstract ClassLoader getClassLoader();
public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass) { ... }
public final String getString(@StringRes int resId) { ... }
public final int getColor(@ColorRes int id) { ... }
public final Drawable getDrawable(@DrawableRes int id) { ... }
public abstract Resources.Theme getTheme();
public abstract void setTheme(@StyleRes int resid);
public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { ... }
// 获取应用相关信息
public abstract ApplicationInfo getApplicationInfo();
public abstract String getPackageName();
public abstract Looper getMainLooper();
public abstract int checkPermission(@NonNull String permission, int pid, int uid);
// 文件相关
public abstract File getSharedPreferencesPath(String name);
public abstract File getDataDir();
public abstract boolean deleteFile(String name);
public abstract File getExternalFilesDir(@Nullable String type);
public abstract File getCacheDir();
...
public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);
public abstract boolean deleteSharedPreferences(String name);
// 数据库相关
public abstract SQLiteDatabase openOrCreateDatabase(...);
public abstract boolean deleteDatabase(String name);
public abstract File getDatabasePath(String name);
...
// 其它
public void registerComponentCallbacks(ComponentCallbacks callback) { ... }
public void unregisterComponentCallbacks(ComponentCallbacks callback) { ... }
...
}
public interface ComponentCallbacks {
void onConfigurationChanged(Configuration newConfig);
void onLowMemory();
}
这样一看的话,Context
就相当于 Application
的大管家,主要负责:
- 四大组件的交互,包括启动
Activity
、Broadcast
、Service
,获取ContentResolver
等 - 获取系统/应用资源,包括
AssetManager
、PackageManager
、Resources
、System Service
以及color
、string
、drawable
等 - 文件,包括获取缓存文件夹、删除文件、
SharedPreference
相关等 - 数据库(
SQLite
)相关,包括打开数据库、删除数据库、获取数据库路径等 - 其它辅助功能,比如设置
ComponentCallbacks
,即监听配置信息改变、内存不足等事件的发生
二、ContextImpl 、ContextWrapper、ContextThemeWrapper 有什么区别
2.1 ContextWrapper
2.1.1 部分源码
/**
* 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;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
// 这就是经常让人产生疑惑的 Base Context 了
public Context getBaseContext() {
return mBase;
}
// 下面这些方法全都直接通过 mBase 完成
@Override
public AssetManager getAssets() {
return mBase.getAssets();
}
@Override
public Resources getResources() {
return mBase.getResources();
}
@Override
public PackageManager getPackageManager() {
return mBase.getPackageManager();
}
...
}
ContextWrapper
是 Android 开发中一个关键类,用于包装一个 Context
对象,为视图和其他组件提供更改后的上下文。这在多种开发场景中非常有用,特别是在需要动态更改上下文属性的情况下,如主题和资源。下面将详细讲解 ContextWrapper
的用途、实现原理和使用方法。
2.1.2 ContextWrapper
的基本概念
ContextWrapper
继承自 Context
,其主要功能是持有一个实际的 Context
实例,并将所有 Context
方法的调用委托给这个实例。通过这种方式,可以在不修改原始 Context
的情况下扩展或修改上下文的行为。
2.1.3 主要用途
- 主题更改:可以使用
ContextWrapper
创建一个具有不同主题的上下文,以便在应用的某些部分应用不同的主题。 - 资源重载:可以根据特定条件(如语言或配置更改)重新加载资源。
- 测试:在单元测试中,可以使用
ContextWrapper
模拟不同的上下文环境,以测试代码在不同环境下的表现。 - 自定义上下文行为:可以通过继承
ContextWrapper
并重写其方法,实现自定义的上下文行为。
2.1.4 使用示例
以下是一个使用 ContextWrapper
更改主题的简单示例:
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建一个新的ContextWrapper实例,指定一个自定义主题
ContextWrapper contextWrapper = new ContextThemeWrapper(this, R.style.MyCustomTheme);
// 使用这个上下文来创建一个布局
LayoutInflater layoutInflater = LayoutInflater.from(contextWrapper);
View view = layoutInflater.inflate(R.layout.my_layout, null);
// 将这个视图设置为当前Activity的内容视图
setContentView(view);
}
}
2.1.5 ContextWrapper
的实现原理
ContextWrapper
的核心在于它持有一个 Context
实例,并将所有方法调用委托给这个实例。其实现如下:
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateExceptionBase context already set }
mBase = base;
}
@Override
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();
}
// 其他方法同样委托给 mBase
}
2.1.6 注意事项
- 性能考虑:使用
ContextWrapper
可能会影响性能,特别是在频繁创建和销毁上下文对象时。 - 主题变化:确保布局文件能够正确响应主题的变化,否则可能导致显示问题。
- 内存泄漏:不正确地使用
ContextWrapper
可能导致内存泄漏,特别是在持有对Activity
的引用时。
2.1.7 进阶使用
ContextWrapper
的一个常见子类是 ContextThemeWrapper
,它允许动态更改主题:
public class MyThemeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 动态更改主题
ContextThemeWrapper contextThemeWrapper = new ContextThemeWrapper(this, R.style.AnotherTheme);
// 使用新的上下文来加载布局
LayoutInflater inflater = LayoutInflater.from(contextThemeWrapper);
View view = inflater.inflate(R.layout.my_layout, null);
setContentView(view);
}
}
通过 ContextWrapper
,开发者可以更灵活地管理和操控 Context
,从而更好地应对复杂的应用需求。
2.2 ContextThemeWrapper
2.2.1 部分源码
/**
* 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;
public ContextThemeWrapper() {
super(null);
}
public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
super(base);
mThemeResource = themeResId;
}
public ContextThemeWrapper(Context base, Resources.Theme theme) {
super(base);
mTheme = theme;
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
}
// 在 Recource 初始化之前,传入配置信息
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
if (mResources != null) {
throw new IllegalStateException(...);
}
if (mOverrideConfiguration != null) {
throw new IllegalStateException(...);
}
mOverrideConfiguration = new Configuration(overrideConfiguration);
}
public Configuration getOverrideConfiguration() {
return mOverrideConfiguration;
}
// 没有重写 setResource,即 setResource 行为和父类一样
@Override
public Resources getResources() {
return getResourcesInternal();
}
private Resources getResourcesInternal() {
if (mResources == null) {
if (mOverrideConfiguration == null) {
mResources = super.getResources();
} else {
// 根据配置信息初始化 Resource
// 注意,这里创建了另一个和 Base Context 不同的 Resource
final Context resContext = createConfigurationContext(mOverrideConfiguration);
mResources = resContext.getResources();
}
}
return mResources;
}
@Override
public void setTheme(int resid) {
if (mThemeResource != resid) {
mThemeResource = resid;
initializeTheme();
}
}
private void initializeTheme() {
final boolean first = mTheme == null;
if (first) {
// 根据 Resource 获取 Theme
mTheme = getResources().newTheme();
// 复制内容
final Resources.Theme theme = getBaseContext().getTheme();
if (theme != null) {
mTheme.setTo(theme);
}
}
onApplyThemeResource(mTheme, mThemeResource, first);
}
protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) {
theme.applyStyle(resId, true);
}
@Override
public Resources.Theme getTheme() {
// 只会初始化一次
if (mTheme != null) {
return mTheme;
}
mThemeResource = Resources.selectDefaultTheme(mThemeResource,
getApplicationInfo().targetSdkVersion);
initializeTheme();
return mTheme;
}
...
}
相比 ContextWrapper
,ContextThemeWrapper
有自己的另外 Resource 以及 Theme 成员,并且可以传入配置信息以初始化自己的 Resource 及 Theme。即 Resource 以及 Theme 相关的行为不再是直接调用 mBase
的方法了,也就说,ContextThemeWrapper
和它的 mBase
成员在 Resource 以及 Theme 相关的行为上是不同的。
ContextThemeWrapper
是 Android 中的一个类,继承自 ContextWrapper
,用于在运行时动态更改应用程序或组件的主题。它允许开发者在不重新启动 Activity
的情况下,应用新的主题,从而使应用程序更加灵活和响应式。下面将详细讲解 ContextThemeWrapper
的用途、实现原理和使用方法。
2.2.2 ContextThemeWrapper
的基本概念
ContextThemeWrapper
继承自 ContextWrapper
,提供了一种包装 Context
的方式,使其能够使用不同的主题资源。这对于需要动态更改主题的组件(如对话框、Activity
或自定义视图)非常有用。
2.2.3 主要用途
- 动态主题切换:可以在运行时更改组件的主题,而无需重启
Activity
或Application
。 - 自定义对话框:可以创建具有不同主题的对话框。
- 特定视图的主题应用:可以为特定视图应用不同的主题,而不影响整个
Activity
。
2.2.4 使用示例
以下是一个使用 ContextThemeWrapper
动态更改主题的简单示例:
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建一个新的 ContextThemeWrapper 实例,指定一个自定义主题
ContextThemeWrapper contextThemeWrapper = new ContextThemeWrapper(this, R.style.MyCustomTheme);
// 使用这个上下文来创建一个布局
LayoutInflater layoutInflater = LayoutInflater.from(contextThemeWrapper);
View view = layoutInflater.inflate(R.layout.my_layout, null);
// 将这个视图设置为当前 Activity 的内容视图
setContentView(view);
}
}
2.2.5 ContextThemeWrapper
的实现原理
ContextThemeWrapper
的核心在于它持有一个 Resources.Theme
对象,并将所有与主题相关的方法调用委托给这个对象。其实现如下:
public class ContextThemeWrapper extends ContextWrapper {
private Resources.Theme mTheme;
private int mThemeResource;
public ContextThemeWrapper() {
super(null);
}
public ContextThemeWrapper(Context base, int themeres) {
super(base);
mThemeResource = themeres;
}
public ContextThemeWrapper(Context base, Resources.Theme theme) {
super(base);
mTheme = theme;
}
@Override
protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
theme.applyStyle(resid, true);
}
@Override
public void setTheme(int resid) {
mThemeResource = resid;
initializeTheme();
}
@Override
public Resources.Theme getTheme() {
if (mTheme != null) {
return mTheme;
}
mThemeResource = Resources.selectDefaultTheme(mThemeResource, getApplicationInfo().targetSdkVersion);
initializeTheme();
return mTheme;
}
private void initializeTheme() {
boolean first = mTheme == null;
if (first) {
mTheme = getResources().newTheme();
Resources.Theme theme = getBaseContext().getTheme();
if (theme != null) {
mTheme.setTo(theme);
}
}
onApplyThemeResource(mTheme, mThemeResource, first);
}
}
2.2.6 详细说明
- 构造方法:
ContextThemeWrapper
提供了多个构造方法,可以接受Context
和主题资源 ID 或Resources.Theme
对象。 - 主题初始化:
initializeTheme()
方法用于初始化主题,当主题第一次被请求时,会创建一个新的主题,并将其应用到当前上下文中。 - 应用主题资源:
onApplyThemeResource()
方法用于应用主题资源,通常会通过调用theme.applyStyle()
方法来应用指定的主题样式。
2.2.7 注意事项
- 性能考虑:频繁更改主题可能会影响性能,特别是在复杂布局中。
- 主题一致性:确保布局文件和组件能够正确响应主题的变化,否则可能导致显示问题。
- 内存泄漏:不正确地使用
ContextThemeWrapper
可能导致内存泄漏,特别是在持有对Activity
的引用时。
2.2.8 进阶使用
自定义对话框
使用 ContextThemeWrapper
可以轻松创建具有不同主题的对话框:
public void showCustomDialog() {
ContextThemeWrapper contextThemeWrapper = new ContextThemeWrapper(this, R.style.CustomDialogTheme);
AlertDialog.Builder builder = new AlertDialog.Builder(contextThemeWrapper);
builder.setTitleCustom Dialog .setMessageThis dialog uses a custom theme .setPositiveButtonOK null)
.show();
}
特定视图的主题应用
可以为特定视图应用不同的主题:
public View createCustomView() {
ContextThemeWrapper contextThemeWrapper = new ContextThemeWrapper(this, R.style.CustomViewTheme);
LayoutInflater inflater = LayoutInflater.from(contextThemeWrapper);
return inflater.inflate(R.layout.custom_view_layout, null);
}
通过 ContextThemeWrapper
,开发者可以更灵活地管理和操控 Context
,从而更好地应对复杂的应用需求。这使得应用程序在处理动态主题切换、自定义对话框和特定视图的主题应用时更加得心应手。
2.3 ContextImpl
2.3.1 部分源码
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context {
private int mThemeResource = 0;
private Resources.Theme mTheme = null;
private @NonNull Resources mResources;
// 用于创建 Activity Context
static ContextImpl createActivityContext(...) {
ContextImpl context = new ContextImpl(...);
context.setResources(resourcesManager.createBaseActivityResources(...));
return context;
}
// 用于创建 Application Context、Service Context
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
ContextImpl context = new ContextImpl(...);
context.setResources(packageInfo.getResources());
return context;
}
private static Resources createResources(...) {
return ResourcesManager.getInstance().getResources(...);
}
// ContextThemeWrapper 没有重写父类的 setResources
// 因此会调用 mBase 的 setResources,即和 ContextImpl 的行为一样
void setResources(Resources r) {
if (r instanceof CompatResources) {
((CompatResources) r).setContext(this);
}
mResources = r;
}
@Override
public Resources getResources() {
return mResources;
}
/* ---------- 主题相关 ------------ */
@Override
public void setTheme(int resId) {
synchronized (mSync) {
if (mThemeResource != resId) {
mThemeResource = resId;
initializeTheme();
}
}
}
// 直接创建一个 Theme 对象,相比 ContextThemeWrapper,少了一部分内容
private void initializeTheme() {
if (mTheme == null) {
mTheme = mResources.newTheme();
}
mTheme.applyStyle(mThemeResource, true);
}
@Override
public Resources.Theme getTheme() {
synchronized (mSync) {
// 和 ContextThemeWrapper 基本一样
if (mTheme != null) {
return mTheme;
}
mThemeResource = Resources.selectDefaultTheme(mThemeResource,
getOuterContext().getApplicationInfo().targetSdkVersion);
initializeTheme();
return mTheme;
}
}
}
ContextImpl
和 ContextThemeWrapper
最大的区别就是没有一个 Configuration 而已,其它的行为大致是一样的。另外,ContextImpl
可以用于创建 Activity、Service 以及 Application 的 mBase
成员,这个 Base Context 时除了参数不同,它们的 Resource 也不同。需要注意的是,createActivityContext
等方法中 setResource
是 mBase
自己调用的,Activity、Service 以及 Application 本身并没有执行 setResource
。
ContextImpl
是 Android 框架中的一个内部类,Context
类的具体实现。它提供了应用程序运行时的环境信息,与系统服务、资源、文件系统等进行交互。虽然 ContextImpl
并不是 Android SDK 的公共API,但理解它的工作原理有助于深入了解 Android 的运行机制。
2.3.2 ContextImpl
的基本概念
ContextImpl
是 Context
类的一个具体实现,包含了大量用于管理应用程序资源和系统服务的代码。它通过委托和代理的方式,提供了 Context
接口的具体实现。
2.3.3 主要功能
- 管理系统服务:
ContextImpl
提供了获取系统服务的方法,例如LocationManager
、WindowManager
等。 - 资源访问:提供了访问应用资源(如字符串、图像、布局等)的功能。
- 文件操作:支持应用程序的文件操作,如创建文件、读取和写入文件等。
- 广播机制:支持发送和接收广播。
- 启动活动:支持启动新的
Activity
和服务。
2.3.4 ContextImpl
的实现
ContextImpl
的实现包含了大量与系统服务和资源管理相关的代码。以下是 ContextImpl
的简化实现:
public class ContextImpl extends Context {
private static final String TAG =ContextImpl private final LoadedApk mPackageInfo;
private final IBinder mActivityToken;
private final UserHandle mUser;
private final Resources mResources;
private final ResourcesManager mResourcesManager;
// 构造函数
ContextImpl(LoadedApk packageInfo, IBinder activityToken, UserHandle user, Resources resources, ResourcesManager resourcesManager) {
mPackageInfo = packageInfo;
mActivityToken = activityToken;
mUser = user;
mResources = resources;
mResourcesManager = resourcesManager;
}
@Override
public Resources getResources() {
return mResources;
}
@Override
public PackageManager getPackageManager() {
return mPackageInfo.getApplication().getPackageManager();
}
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
@Override
public void startActivity(Intent intent) {
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeExceptionCalling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag }
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null, (Activity)null, intent, -1);
}
@Override
public void sendBroadcast(Intent intent) {
mMainThread.sendBroadcast(intent, null);
}
// 更多方法实现...
}
2.3.5 详细说明
- 构造方法:
ContextImpl
的构造方法初始化了包信息(LoadedApk
)、活动标识(IBinder
)、用户信息(UserHandle
)、资源(Resources
)和资源管理器(ResourcesManager
)。 - 资源访问:
getResources()
方法返回Resources
对象,用于访问应用的资源。 - 系统服务:
getSystemService()
方法通过SystemServiceRegistry
获取并返回系统服务对象。 - 内容解析:
getContentResolver()
方法返回应用的ContentResolver
,用于访问内容提供者。 - 启动活动:
startActivity()
方法用于启动新的活动,要求Intent
必须包含FLAG_ACTIVITY_NEW_TASK
标志。 - 发送广播:
sendBroadcast()
方法用于发送广播消息。
2.3.6 进阶使用
虽然 ContextImpl
是 Android 内部实现的一部分,开发者通常不会直接使用它,但理解其工作原理对解决复杂的上下文相关问题非常有用。以下是一些进阶使用场景:
系统服务管理
ContextImpl
提供了获取系统服务的方法,可以通过以下方式获取系统服务:
public LocationManager getLocationManager() {
return (LocationManager) getSystemService(Context.LOCATION_SERVICE);
}
资源管理
通过 ContextImpl
可以访问应用的资源,例如字符串、图像和布局:
public String getAppName() {
return getResources().getString(R.string.app_name);
}
文件操作
ContextImpl
支持文件操作,可以通过以下方式创建和读取文件:
public void createFile() {
File file = new File(getFilesDir(),example.txt try (FileOutputStream fos = new FileOutputStream(file)) {
fos.writeHello, WorldgetBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
2.3.7 实践中的应用
尽管 ContextImpl
不直接暴露给开发者,但理解其原理有助于更好地使用 Context
类的各种功能。例如,开发者可以通过 Context
获取系统服务、启动活动和访问资源,而这些功能都是通过 ContextImpl
实现的。
通过深入理解 ContextImpl
,开发者可以更好地掌握 Android 应用的运行机制,提高应用开发的效率和质量。
2.4 Activity Context、Service Context、Application Context、Base Context 的区别
Activity Context
Activity启动时最终会调用 ActivityThread
的performLaunchActivity
public final class ActivityThread {
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
// 这个 Context 将会作为 Activity 的 Base Context
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
ClassLoader cl = appContext.getClassLoader();
// 创建 Activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
} catch (Exception e) {
...
}
try {
// 创建 Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
// 初始化 Activity,注意参数 appContext
activity.attach(appContext, ...);
...
}
} catch (...) {
...
}
return activity;
}
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
ContextImpl appContext = ContextImpl.createActivityContext(...);
...
}
}
Activity 的 Base Context
就是上面分析过的 ContextImpl
的 createActivityContext
创建的
Application Context
Service 的 Base Context
的创建过程和 Application 一样,调用的都是 ContextImpl
的 createAppContext
,即 Service Context 和 Application Context 的 Resource 是相同的。因此这里跳过 Service,下面看一下 Application Context。
在上面 ActivityThread
的 performLaunchActivity
方法中,可以看到一个 makeApplication
的调用,它是 LoaedApk
的方法:
public final class LoadedApk {
private Application mApplication;
public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Application app = null;
try {
// 创建 Base Context
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
// 创建 Application 并设置 Base Context
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
...
}
// Application 创建成功,赋值给 mApplication
mApplication = app;
...
return app;
}
// 获取 mApplication
Application getApplication() {
return mApplication;
}
}
newApplication
实现:
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
//-----1-----
app.attach(context);
return app;
}
public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
@NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (Application) cl.loadClass(className).newInstance();
}
接着进入上面注释1
处的app.attach(context)
:
@UnsupportedAppUsage
/* package */ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
进入attachBaseContext(context)
, 因为Application继承ContextWrapper, 这里会进入ContextWrapper的attachBaseContext
中, 将base赋值给ContextWrapper中的mBase
。注意这里的context是在makeApplication
方法中通过ContextImpl.createAppContext
创建的,所以是一个ContextImpl
类型的Context,这也印证了最开始说的mBase
是ContextImpl类型, 到这里Application的Context创建就结束了。
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
- Application中获取getApplicationContext()
- 在Application中调用
getApplicationContext()
实际进入到的是ContextWrapper
中的getApplicationContext()
,@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
} - 这个
mBase
就是ContextImpl
, 所以我们进入到ContextImpl
中@Override
public Context getApplicationContext() {
return (mPackageInfo != null) ?
mPackageInfo.getApplication() : mMainThread.getApplication();
} - 这里
mPackageInfo
是LoadedApk
, 这里肯定不为null, 从本文最开始ActivityThread.performLaunchActivity
中开始,先判断if (r.packageInfo == null)
的话就会给其赋值,而这里的mPackageInfo
就是在创建ContextImpl
的时候从那里传过来的。这样我们就进入到mPackageInfo.getApplication()
中:Application getApplication() {
return mApplication;
} - 这样我们通过getApplicationContext()就得到了全局唯一的
mApplication
。
- 在Application中调用
为什么不推荐使用 Base Context
一般情况下,使用代理而不直接使用某个对象,目的可能有两个:
- 定制自己的行为
- 不影响原对象
其中 Service 和 Application 的父类 ContextWrapper
完全没有自定义的行为,而 Activity 的父类 ContextThemeWrapper
则自定义了 Resource 以及 Theme 的相关行为,因此,个人理解:
- 对于 Service 和 Application 而言,不推荐使用 Base Context,是担心用户修改了 Base Context 而导致错误的发生
- 对于 Activity 而言,除了担心用户的修改之外,Base Context 和 Activity 本身对于 Resource以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话),使用 Base Context 可能会出现无法预期的现象
小结
- Activity、Service 和 Application 的 Base Context 都是由
ContextImpl
创建的,且创建的都是ContextImpl
对象,即它们都是ContextImpl
的代理类 getApplicationContext
返回的就是 Application 对象本身,一般情况下它对应的是应用本身的 Application 对象,但也可能是系统的某个 Application- 对于 Service 和 Application 而言,不推荐使用 Base Context,是担心用户修改了 Base Context 而导致错误的发生
- 对于 Activity 而言,除了担心用户的修改之外,Base Context 和 Activity 本身对于 Resource 以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话),使用 Base Context 可能会出现无法预期的现象
2.5 总结
Context 相当于 Application 的大管家,主要负责:
- 四大组件的交互,包括启动 Activity、Broadcast、Service,获取 ContentResolver 等
- 获取系统/应用资源,包括
AssetManager
、PackageManager
、Resources、System Service 以及 color、string、drawable 等 - 文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等
- 数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等
- 其它辅助功能,比如设置
ComponentCallbacks
,即监听配置信息改变、内存不足等事件的发生
ContextWrapper
、ContextThemeWrapper
、ContextImpl
的区别:
ContextWrapper
、ContextThemeWrapper
都是 Context 的代理类,二者的区别在于ContextThemeWrapper
有自己的 Theme 以及 Resource,并且 Resource 可以传入自己的配置初始化ContextImpl
是 Context 的主要实现类,Activity、Service 和 Application 的 Base Context 都是由它创建的,即ContextWrapper
代理的就是ContextImpl
对象本身ContextImpl
和ContextThemeWrapper
的主要区别是,ContextThemeWrapper
有 Configuration 对象,Resource 可以根据这个对象来初始化
Activity Context、Service Context、Application Context、Base Context 的区别:
- Activity、Service 和 Application 的 Base Context 都是由
ContextImpl
创建的,且创建的都是ContextImpl
对象,即它们都是ContextImpl
的代理类 - Service 和 Application 使用同一个 Recource,和 Activity 使用的 Resource 不同
getApplicationContext
返回的就是 Application 对象本身,一般情况下它对应的是应用本身的 Application 对象,但也可能是系统的某个 Application