深入理解 Context
什么是Context?
想必大家都不陌生,在 Android 开发中离不开 Context 调用各种跟系统有关的 API 都必须用到 Context 。我们可以将她理解为上下文环境,大概就是里面存储一堆全局变量,这些变量在调用系统 API 时需要用到。文字始终难以表达我想说的,咱们来分析原理吧!Context 哪里来的?
开发 Android 应用必须得有一个 Activity ,然后在你的 Activity 里面调用各种系统 API 比如,String string = getString(R.string.app_name);这行代码看似简单,但是如果不让你在 Activity 中调用你该怎么获取这个 string ?
Context context; context.getString(R.string.app_name);你需要一个 Context 对象,那在 Activity 中为何不需要呢? 答案是,Activity 本来就是一个 Context 他继承自 Context,你打开 Activity 可以知道他的继承顺序如下: Activity -> ContextThemeWrapper -> ContextWrapper -> Context
Context 是一个抽象类,所有方法都没实现,ContextWrapper 实现了所有方法,但是他只是一个代理,打开源码你可以看到他是这样的:
@Override public AssetManager getAssets() { return mBase.getAssets(); } @Override public Resources getResources() { return mBase.getResources(); }他仅仅调用 mBase 的方法,(这种在设计模式里叫 代理模式 Proxy ),看来我们得弄清楚这个 mBase 哪来的了! 查看源码你会发现只有2个地方设置了 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; }一是构造函数,一是 attachBaseContext 。 我们先来分析构造函数!Activity 继承自她,所以分析得 Activity 怎么创建的,由于 Activity 的创建过程比较复杂,而此篇重点不是他,所有我在这里直接给出结果,Activity 创建的时候调用的是无参数构造函数。所以 mBase 不是来自构造函数,那就是来自 attachBaseContext 了,下面我们来分析 Activity 在哪里调用了 attachBaseContext() 方法: 查阅 Activity 源码可以找到一处调用
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) { attachBaseContext(context);就是 Activity.attach 方法,此方法在 framework 创建 Activity 的时候调用的,要知道这个 context 参数是什么,我们得从 framework 代码去看, 代码:android.app.ActivityThread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { Activity activity = null; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); } catch (Exception e) { } try { if (activity != null) { ContextImpl appContext = new ContextImpl(); appContext.init(r.packageInfo, r.token, this); appContext.setOuterContext(activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config);看到了,context 就是此处的 appContext 他是 ContextImpl 对象,好了,现在我们知道 ContextImpl 才是真正的实现者了。 所以 getString() 的真正调用是 ContextImpl.getResource.getString() ,getString() 的真正实现在 Resource (android.content.res.Resources) 。
有哪几种 Context?
- Activity :刚刚我们已经分析
- Service :也是继承自 ContextWrapper 跟 Activity 一样
- BroadcastReceiver : onReceiver(Context context )
- Application:
那么什么样的 Context 才能做全局引用呢? 答案是:Application ,如果你没有自定义的 Application 怎么获取这个 Context 呢,你会发现 Context 有一个 getApplicationContext() 方法,他在 ContextImpl 的实现如下:
android.app.ContextImpl
@Override public Context getApplicationContext() { return (mPackageInfo != null) ? mPackageInfo.getApplication() : mMainThread.getApplication(); }
android.app.LoadedApk
Application getApplication() { return mApplication; }
这个mApplication 对象就是程序的 Application 。所以你可以把他强转成你自定义的 Application
BroadcastReceiver 的 Context
Application app = packageInfo.makeApplication(false, mInstrumentation); if (localLOGV) Slog.v( TAG, "Performing receive of " + data.intent + ": app=" + app + ", appName=" + app.getPackageName() + ", pkg=" + packageInfo.getPackageName() + ", comp=" + data.intent.getComponent().toShortString() + ", dir=" + packageInfo.getAppDir()); ContextImpl context = (ContextImpl)app.getBaseContext(); sCurrentBroadcastIntent.set(data.intent); receiver.setPendingResult(data); receiver.onReceive(context.getReceiverRestrictedContext(), data.intent);
从代码得知,他是 Application 的 ContextImpl 对象,所以他是全局的
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。
- 上一篇: 求一个3*3矩阵对角线元素之和
- 下一篇: 黑窗口下mysql导出导入数据库