Context 学习笔记
本文最后更新于 82 天前,其中的信息可能已经有所发展或是发生改变,请谨慎参考。

Context 本身是一个抽象类,主要实现类为 ContextImpl,另外有子类 ContextWrapperContextThemeWrapper,这两个子类都是 Context 的代理类,主要区别是 ContextThemeWrapper 有自己的主题资源。它们继承关系如下::

image-20240613171159051

一、Context有什么用

先来看下官方对 Context 的定义:Context是一个由Android系统提供实现的抽象类,它是一个接口,允许你访问和操作应用程序环境的全局信息。这些信息和操作包括:

  1. 访问特定应用程序的资源和类,例如,你可以通过Context来访问应用程序的布局资源、字符串资源等。
  2. 执行应用程序级别的操作,例如启动活动、发送和接收广播等。

因此,无论你是在哪个部分的代码中,只要你有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 的大管家,主要负责:

  1. 四大组件的交互,包括启动 ActivityBroadcastService,获取 ContentResolver
  2. 获取系统/应用资源,包括 AssetManagerPackageManagerResourcesSystem Service 以及 colorstringdrawable
  3. 文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等
  4. 数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等
  5. 其它辅助功能,比如设置 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 主要用途

  1. 主题更改:可以使用 ContextWrapper 创建一个具有不同主题的上下文,以便在应用的某些部分应用不同的主题。
  2. 资源重载:可以根据特定条件(如语言或配置更改)重新加载资源。
  3. 测试:在单元测试中,可以使用 ContextWrapper 模拟不同的上下文环境,以测试代码在不同环境下的表现。
  4. 自定义上下文行为:可以通过继承 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;
}

...
}

相比 ContextWrapperContextThemeWrapper 有自己的另外 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 主要用途

  1. 动态主题切换:可以在运行时更改组件的主题,而无需重启 ActivityApplication
  2. 自定义对话框:可以创建具有不同主题的对话框。
  3. 特定视图的主题应用:可以为特定视图应用不同的主题,而不影响整个 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 详细说明

  1. 构造方法ContextThemeWrapper 提供了多个构造方法,可以接受 Context 和主题资源 ID 或 Resources.Theme 对象。
  2. 主题初始化initializeTheme() 方法用于初始化主题,当主题第一次被请求时,会创建一个新的主题,并将其应用到当前上下文中。
  3. 应用主题资源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;
}
}
}

ContextImplContextThemeWrapper 最大的区别就是没有一个 Configuration 而已,其它的行为大致是一样的。另外,ContextImpl 可以用于创建 Activity、Service 以及 Application 的 mBase 成员,这个 Base Context 时除了参数不同,它们的 Resource 也不同。需要注意的是,createActivityContext 等方法中 setResourcemBase 自己调用的,Activity、Service 以及 Application 本身并没有执行 setResource

ContextImpl 是 Android 框架中的一个内部类,Context 类的具体实现。它提供了应用程序运行时的环境信息,与系统服务、资源、文件系统等进行交互。虽然 ContextImpl 并不是 Android SDK 的公共API,但理解它的工作原理有助于深入了解 Android 的运行机制。

2.3.2 ContextImpl 的基本概念

ContextImplContext 类的一个具体实现,包含了大量用于管理应用程序资源和系统服务的代码。它通过委托和代理的方式,提供了 Context 接口的具体实现。

2.3.3 主要功能

  1. 管理系统服务ContextImpl 提供了获取系统服务的方法,例如 LocationManagerWindowManager 等。
  2. 资源访问:提供了访问应用资源(如字符串、图像、布局等)的功能。
  3. 文件操作:支持应用程序的文件操作,如创建文件、读取和写入文件等。
  4. 广播机制:支持发送和接收广播。
  5. 启动活动:支持启动新的 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 详细说明

  1. 构造方法ContextImpl 的构造方法初始化了包信息(LoadedApk)、活动标识(IBinder)、用户信息(UserHandle)、资源(Resources)和资源管理器(ResourcesManager)。
  2. 资源访问getResources() 方法返回 Resources 对象,用于访问应用的资源。
  3. 系统服务getSystemService() 方法通过 SystemServiceRegistry 获取并返回系统服务对象。
  4. 内容解析getContentResolver() 方法返回应用的 ContentResolver,用于访问内容提供者。
  5. 启动活动startActivity() 方法用于启动新的活动,要求 Intent 必须包含 FLAG_ACTIVITY_NEW_TASK 标志。
  6. 发送广播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启动时最终会调用 ActivityThreadperformLaunchActivity

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 就是上面分析过的 ContextImplcreateActivityContext 创建的

Application Context

Service 的 Base Context 的创建过程和 Application 一样,调用的都是 ContextImplcreateAppContext即 Service Context 和 Application Context 的 Resource 是相同的。因此这里跳过 Service,下面看一下 Application Context。

在上面 ActivityThreadperformLaunchActivity 方法中,可以看到一个 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();
      }
    • 这里mPackageInfoLoadedApk, 这里肯定不为null, 从本文最开始ActivityThread.performLaunchActivity中开始,先判断if (r.packageInfo == null)的话就会给其赋值,而这里的mPackageInfo就是在创建ContextImpl的时候从那里传过来的。这样我们就进入到mPackageInfo.getApplication()中:Application getApplication() {
      return mApplication;
      }
    • 这样我们通过getApplicationContext()就得到了全局唯一的mApplication

为什么不推荐使用 Base Context

一般情况下,使用代理而不直接使用某个对象,目的可能有两个:

  1. 定制自己的行为
  2. 不影响原对象

其中 Service 和 Application 的父类 ContextWrapper 完全没有自定义的行为,而 Activity 的父类 ContextThemeWrapper 则自定义了 Resource 以及 Theme 的相关行为,因此,个人理解:

  1. 对于 Service 和 Application 而言,不推荐使用 Base Context,是担心用户修改了 Base Context 而导致错误的发生
  2. 对于 Activity 而言,除了担心用户的修改之外,Base Context 和 Activity 本身对于 Resource以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话),使用 Base Context 可能会出现无法预期的现象

小结

  1. Activity、Service 和 Application 的 Base Context 都是由 ContextImpl 创建的,且创建的都是 ContextImpl 对象,即它们都是 ContextImpl 的代理类
  2. getApplicationContext 返回的就是 Application 对象本身,一般情况下它对应的是应用本身的 Application 对象,但也可能是系统的某个 Application
  3. 对于 Service 和 Application 而言,不推荐使用 Base Context,是担心用户修改了 Base Context 而导致错误的发生
  4. 对于 Activity 而言,除了担心用户的修改之外,Base Context 和 Activity 本身对于 Resource 以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话),使用 Base Context 可能会出现无法预期的现象

2.5 总结

Context 相当于 Application 的大管家,主要负责:

  1. 四大组件的交互,包括启动 Activity、Broadcast、Service,获取 ContentResolver 等
  2. 获取系统/应用资源,包括 AssetManagerPackageManager、Resources、System Service 以及 color、string、drawable 等
  3. 文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等
  4. 数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等
  5. 其它辅助功能,比如设置 ComponentCallbacks,即监听配置信息改变、内存不足等事件的发生

ContextWrapperContextThemeWrapperContextImpl 的区别:

  1. ContextWrapperContextThemeWrapper 都是 Context 的代理类,二者的区别在于 ContextThemeWrapper 有自己的 Theme 以及 Resource,并且 Resource 可以传入自己的配置初始化
  2. ContextImpl 是 Context 的主要实现类,Activity、Service 和 Application 的 Base Context 都是由它创建的,即 ContextWrapper 代理的就是 ContextImpl 对象本身
  3. ContextImplContextThemeWrapper 的主要区别是, ContextThemeWrapper 有 Configuration 对象,Resource 可以根据这个对象来初始化

Activity Context、Service Context、Application Context、Base Context 的区别:

  1. Activity、Service 和 Application 的 Base Context 都是由 ContextImpl 创建的,且创建的都是 ContextImpl 对象,即它们都是 ContextImpl 的代理类
  2. Service 和 Application 使用同一个 Recource,和 Activity 使用的 Resource 不同
  3. getApplicationContext 返回的就是 Application 对象本身,一般情况下它对应的是应用本身的 Application 对象,但也可能是系统的某个 Application

三、参考链接

您当前正在 - https://icu007.work/archives/594 .页面,阅读由“Rookie_L” 撰写的《Context 学习笔记》
非常感谢您对我们的网站感兴趣并访问。在您使用本网站之前,请您仔细阅读本声明的所有条款。

版权声明:
1、本博客属个人所有,不涉及商业目的;
2、本博客内容均为本人编写,图片版权属于原作者,图片仅供大家欣赏和分享,切勿做为商业目的使用。如果侵害了您的合法权益,请您及时与我联系,我会在第一时间删除相关内容;
3、本博客所有原创作品,包括文字、资料、图片、网页格式,转载时请标注作者与来源。非经允许,不得用于盈利目的;
4、本博客受中国知识产权、互联网法规和知识共享条例保护和保障,任何人不得进行旨在破坏或牟取私利的行为;
5、做新时代合格网民,弘扬互联网精神:开放、平等、 协作 、分享;共同构建文明、清朗的网络环境;
6、本声明未涉及的问题参见国家有关法律法规,当本声明与国家法律法规冲突时,以国家法律法规为准;
7、当您阅读到这里的时候,即表明已阅读并接受了上述各项条款。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇