前言
网上关于 Context 的文章也已经有不少了, 比如值得参考的有:
Android Context 完全解析, 你所不知道的 Context 的各种细节
Android Context 到底是什么?
但看了一下, 发现还有值得讨论的地方, 比如这个等式:
Context 个数 = Service 个数 + Activity 个数 + 1
老实说, 我不明白这个等式有什么意义, 而且还是错的. 首先多进程情况下, Application 对象就不止一个; 其次, Activity,Service,Application 继承自 ContextWrapper, 它们自己就是一个 Context, 里面又有一个 Base Context; 最后, 还有各种 outer context,display context 什么的, 这部分没深入研究过, 但 Context 的数量绝对大于上述等式的两倍了.
上面这部分算一个讨论, 下面正式进入正题.
Context 家族
Context 本身是一个抽象类, 主要实现类为 ContextImpl, 另外有子类 ContextWrapper 和 ContextThemeWrapper, 这两个子类都是 Context 的代理类, 主要区别是 ContextThemeWrapper 有自己的主题资源. 它们继承关系如下:
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 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 Intent service,
- @NonNull ServiceConnection conn, @BindServiceFlags 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 有什么区别?
ContextWrapper
先看 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 {
- // 注意这个成员
- 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 实际上就是 Context 的代理类而已, 所有的操作都是通过内部成员 mBase 完成的, 另外, Activity,Service 的 getBaseContext 返回的就是这个 mBase.
ContextThemeWrapper
接着看 ContextThemeWrapper, 这个类的代码并不多, 主要看 Resource 和 Theme 相关的:
- /**
- * 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 相关的行为上是不同的.
ContextImpl
下面看一下 ContextImpl 有关 Theme 以及 Resource 的部分, 以分析它和 ContextThemeWrapper 的区别:
- /**
- * 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();
- }
- }
- }
- // 直接创建一个 Themem 对象, 相比 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.
小结
ContextWrapper,ContextThemeWrapper 都是 Context 的代理类, 二者的区别在于 ContextThemeWrapper 有自己的 Theme 以及 Resource, 并且 Resource 可以传入自己的配置初始化
ContextImpl 是 Context 的主要实现类, Activity,Service 和 Application 的 Base Context 都是由它创建的, 即 ContextWrapper 代理的就是 ContextImpl 对象本身
ContextImpl 和 ContextThemeWrapper 的主要区别是, ContextThemeWrapper 有 Configuration 对象, Resource 可以根据这个对象来初始化
Service 和 Application 使用同一个 Recource, 和 Activity 使用的 Resource 不同
Activity Context,Service Context,Application Context,Base Context 有什么区别?
Activity Context
先看 Activity,Activity 在启动时, 最终会执行 ActivityThread 的 performLaunchActivitiy:
- 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 创建的.
同时, Service 的 Base Context 的创建过程和 Application 一样, 调用的都是 ContextImpl 的 createAppContext, 即 Service Context 和 Application Context 的 Resource 是相同的. 因此这里跳过 Service, 下面看一下 Application Context.
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;
- }
- }
看完上面的代码, 其实已基本能猜出 Application 及其 Base Context 的创建过程了, 但为了完整地了解整个过程, 下面还是看一下 Instrumentation 的实现:
- public class Instrumentation {
- public Application newApplication(ClassLoader cl, String className, Context context) throws ... {
- return newApplication(cl.loadClass(className), context);
- }
- static public Application newApplication(Class<?> clazz, Context context) throws ... {
- // 反射创建 Application 对象
- Application App = (Application)clazz.newInstance();
- App.attach(context);
- return App;
- }
- }
- public class Application extends ContextWrapper implements ComponentCallbacks2 {
- /* package */ final void attach(Context context) {
- // 调用父类的 attachBaseContext 以设置 mBase
- attachBaseContext(context);
- }
- }
可以看到, Instrumentation 是使用反射的方法创建 Application 对象, 创建完毕后, 会执行 Application 的 attach 方法设置 mBase 成员.
Application 及其 Base Context 的创建过程我们了解了, 接下来看一下 getApplicationContext 的实现:
- class ContextImpl extends Context {
- ActivityThread mMainThread;
- LoadedApk mPackageInfo;
- @Override
- public Context getApplicationContext() {
- return (mPackageInfo != null) ?
- mPackageInfo.getApplication() : mMainThread.getApplication();
- }
- }
从代码中可以看出, getApplicationContext 的返回值可能有两个: 第一个是 LoadedApk 的 getApplication 方法, 这个方法的返回值就是刚刚创建的 Application 对象; 第二个是 ActivityThread 的 getApplication 方法:
- public final class ActivityThread {
- Application mInitialApplication;
- public Application getApplication() {
- return mInitialApplication;
- }
- public static ActivityThread systemMain() {
- // 创建 ActivityThread
- ActivityThread thread = new ActivityThread();
- thread.attach(true);
- return thread;
- }
- private void attach(boolean system) {
- mSystemThread = system;
- if (!system) {
- ...
- } else {
- try {
- mInstrumentation = new Instrumentation();
- // 注意参数 getSystemContext().mPackageInfo
- ContextImpl context = ContextImpl.createAppContext(
- this, getSystemContext().mPackageInfo);
- // 创建 Application
- mInitialApplication = context.mPackageInfo.makeApplication(true, null);
- mInitialApplication.onCreate();
- } catch (Exception e) {
- ...
- }
- }
- ...
- }
- }
ActivityThread 中的 mInitialApplication 是在 systemMain 方法执行时创建的, 而这个方法又是 SystemServer 启动时调用的, 结合参数 getSystemContext().mPackageInfo, 因此个人推测 mInitialApplication 对应的是系统的某个 apk, 即系统级别的 Application, 但具体是不是这样, 目前还没有深入研究过, 有兴趣的可以自己研究.
为什么不推荐使用 Base Context?
一般情况下, 使用代理而不直接使用某个对象, 目的可能有两个:
定制自己的行为
不影响原对象
其中 Servcie 和 Application 的父类 ContextWrapper 完全没有自定义的行为, 而 Activity 的父类 ContextThemeWrapper 则自定义了 Resource 以及 Theme 的相关行为, 因此, 个人理解:
对于 Service 和 Application 而言, 不推荐使用 Base Context, 是担心用户修改了 Base Context 而导致错误的发生
对于 Activity 而言, 除了担心用户的修改之外, Base Context 和 Activity 本身对于 Reource 以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话), 使用 Base Context 可能会出现无法预期的现象
对于 Activity 的 getResource 问题, 我写了一份代码来验证:
- public class MainActivity extends AppCompatActivity {
- private Configuration mOverrideConfiguration;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Log.i(TAG, "getResources:" + getResources() + ", getBaseContext().getResources():"
- + getBaseContext().getResources());
- }
- // 因为 Android 会在 onCreate 之前自动调用 getResource
- // 因此需要在这里执行 applyOverrideConfiguration
- @Override
- public Resources getResources() {
- if (mOverrideConfiguration == null) {
- mOverrideConfiguration = new Configuration();
- applyOverrideConfiguration(mOverrideConfiguration);
- }
- return super.getResources();
- }
- }
输出(我用的是小米手机):
- getResources: Android.content.res.MiuiResources@3c660a7,
- getBaseContext().getResources():Android.content.res.MiuiResources@5143954
可以看到, 就像源码显示的那样, 应用了 Configuration 之后, Activity 的 getResource 方法返回的和 getBaseContext().getResources() 方法返回的不是同一个对象
小结
Activity,Service 和 Application 的 Base Context 都是由 ContextImpl 创建的, 且创建的都是 ContextImpl 对象, 即它们都是 ContextImpl 的代理类
getApplicationContext 返回的就是 Application 对象本身, 一般情况下它对应的是应用本身的 Application 对象, 但也可能是系统的某个 Application
对于 Service 和 Application 而言, 不推荐使用 Base Context, 是担心用户修改了 Base Context 而导致错误的发生
对于 Activity 而言, 除了担心用户的修改之外, Base Context 和 Activity 本身对于 Reource 以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话), 使用 Base Context 可能会出现无法预期的现象
总结
Context 的继承关系如下:
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
为什么不推荐使用 Base Context:
对于 Service 和 Application 而言, 不推荐使用 Base Context, 是担心用户修改了 Base Context 而导致错误的发生
对于 Activity 而言, 除了担心用户的修改之外, Base Context 和 Activity 本身对于 Reource 以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话), 使用 Base Context 可能出现无法预期的现象
来源: https://juejin.im/post/5c1fab7d5188254eb05fbe48