内容简介:但看了一下,发现还有值得讨论的地方,比如这个等式:Context个数 = Service 个数 + Activity 个数 + 1老实说,我不明白这个等式有什么意义,而且还是错的。首先多进程情况下,Application 对象就不止一个;其次,Activity、Service、Application 继承自 ContextWrapper,它们自己就是一个 Context,里面又有一个 Base Context;最后,还有各种 outer context、display 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 可能出现无法预期的现象
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 【1】JavaScript 基础深入——数据类型深入理解与总结
- 深入理解java虚拟机(1) -- 理解HotSpot内存区域
- 深入理解 HTTPS
- 深入理解 HTTPS
- 深入理解 SecurityConfigurer
- 深入理解 HTTP 协议
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。