关闭

深入理解 Context

109人阅读 评论(0) 收藏 举报
分类:
什么是Context?
想必大家都不陌生,在 Android 开发中离不开 Context 调用各种跟系统有关的 API 都必须用到 Context 。我们可以将她理解为上下文环境,大概就是里面存储一堆全局变量,这些变量在调用系统 API 时需要用到。文字始终难以表达我想说的,咱们来分析原理吧!

Context  哪里来的?
开发 Android 应用必须得有一个 Activity ,然后在你的 Activity 里面调用各种系统 API 比如,
[java] view plain copy
  1. String string = getString(R.string.app_name);  
这行代码看似简单,但是如果不让你在 Activity 中调用你该怎么获取这个 string ?
[java] view plain copy
  1. Context context;  
  2. context.getString(R.string.app_name);  
你需要一个 Context 对象,那在 Activity 中为何不需要呢?
答案是,Activity 本来就是一个 Context 他继承自 Context,你打开 Activity 可以知道他的继承顺序如下:
Activity -> ContextThemeWrapper -> ContextWrapper -> Context

Context 是一个抽象类,所有方法都没实现,ContextWrapper 实现了所有方法,但是他只是一个代理,打开源码你可以看到他是这样的:
[java] view plain copy
  1. @Override  
  2. public AssetManager getAssets() {  
  3.     return mBase.getAssets();  
  4. }  
  5.   
  6. @Override  
  7. public Resources getResources()  
  8. {  
  9.     return mBase.getResources();  
  10. }  
他仅仅调用 mBase 的方法,(这种在设计模式里叫 代理模式 Proxy ),看来我们得弄清楚这个 mBase 哪来的了!
查看源码你会发现只有2个地方设置了 mBase,
[java] view plain copy
  1. public ContextWrapper(Context base) {  
  2.     mBase = base;  
  3. }  
  4.   
  5. /** 
  6.  * Set the base context for this ContextWrapper.  All calls will then be 
  7.  * delegated to the base context.  Throws 
  8.  * IllegalStateException if a base context has already been set. 
  9.  *  
  10.  * @param base The new base context for this wrapper. 
  11.  */  
  12. protected void attachBaseContext(Context base) {  
  13.     if (mBase != null) {  
  14.         throw new IllegalStateException("Base context already set");  
  15.     }  
  16.     mBase = base;  
  17. }  
一是构造函数,一是 attachBaseContext 。
我们先来分析构造函数!Activity 继承自她,所以分析得 Activity 怎么创建的,由于 Activity 的创建过程比较复杂,而此篇重点不是他,所有我在这里直接给出结果,Activity 创建的时候调用的是无参数构造函数。所以 mBase 不是来自构造函数,那就是来自 attachBaseContext 了,下面我们来分析 Activity 在哪里调用了 attachBaseContext() 方法:
查阅 Activity 源码可以找到一处调用
[java] view plain copy
  1. final void attach(Context context, ActivityThread aThread,  
  2.         Instrumentation instr, IBinder token, int ident,  
  3.         Application application, Intent intent, ActivityInfo info,  
  4.         CharSequence title, Activity parent, String id,  
  5.         NonConfigurationInstances lastNonConfigurationInstances,  
  6.         Configuration config) {  
  7.     attachBaseContext(context);  
就是 Activity.attach 方法,此方法在 framework 创建 Activity 的时候调用的,要知道这个 context 参数是什么,我们得从 framework 代码去看,
代码:android.app.ActivityThread 
[java] view plain copy
  1. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
  2.       
  3.     Activity activity = null;  
  4.     try {  
  5.         java.lang.ClassLoader cl = r.packageInfo.getClassLoader();  
  6.         activity = mInstrumentation.newActivity(  
  7.                 cl, component.getClassName(), r.intent);             
  8.     } catch (Exception e) {  
  9.     }  
  10.   
  11.     try {  
  12.         if (activity != null) {  
  13.             ContextImpl appContext = new ContextImpl();  
  14.             appContext.init(r.packageInfo, r.token, this);  
  15.             appContext.setOuterContext(activity);  
  16.   
  17.             CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());  
  18.             Configuration config = new Configuration(mCompatConfiguration);  
  19.            
  20.             activity.attach(appContext, this, getInstrumentation(), r.token,  
  21.                     r.ident, app, r.intent, r.activityInfo, title, r.parent,  
  22.                     r.embeddedID, r.lastNonConfigurationInstances, config);  
看到了,context 就是此处的 appContext 他是 ContextImpl 对象,好了,现在我们知道 ContextImpl 才是真正的实现者了。
所以 getString() 的真正调用是 ContextImpl.getResource.getString() ,getString() 的真正实现在 Resource (android.content.res.Resources) 。

有哪几种 Context?
  1. Activity :刚刚我们已经分析
  2. Service :也是继承自 ContextWrapper 跟 Activity 一样
  3. BroadcastReceiver : onReceiver(Context context )
  4. Application:
记住所有 Context 真正实现都是 ContextImpl 
Activty、Service 的 Context 都是每次创建的,而不是全局唯一,所以不要将 Activity 、Service 当做全局 Context 引用,这样会导致 Activity 无法销毁,一直被引用者。

那么什么样的 Context 才能做全局引用呢?
答案是:Application ,如果你没有自定义的 Application 怎么获取这个 Context 呢,你会发现 Context 有一个 getApplicationContext() 方法,他在 ContextImpl 的实现如下:

android.app.ContextImpl
[java] view plain copy
  1. @Override  
  2. public Context getApplicationContext() {  
  3.     return (mPackageInfo != null) ?  
  4.             mPackageInfo.getApplication() : mMainThread.getApplication();  
  5. }  

android.app.LoadedApk
[java] view plain copy
  1. Application getApplication() {  
  2.     return mApplication;  
  3. }  

这个mApplication 对象就是程序的 Application 。所以你可以把他强转成你自定义的 Application 

BroadcastReceiver 的 Context
[java] view plain copy
  1. Application app = packageInfo.makeApplication(false, mInstrumentation);  
  2.   
  3. if (localLOGV) Slog.v(  
  4.     TAG, "Performing receive of " + data.intent  
  5.     + ": app=" + app  
  6.     + ", appName=" + app.getPackageName()  
  7.     + ", pkg=" + packageInfo.getPackageName()  
  8.     + ", comp=" + data.intent.getComponent().toShortString()  
  9.     + ", dir=" + packageInfo.getAppDir());  
  10.   
  11. ContextImpl context = (ContextImpl)app.getBaseContext();  
  12. sCurrentBroadcastIntent.set(data.intent);  
  13. receiver.setPendingResult(data);  
  14. receiver.onReceive(context.getReceiverRestrictedContext(),  
  15.         data.intent);  

从代码得知,他是 Application 的 ContextImpl 对象,所以他是全局的

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:46019次
    • 积分:994
    • 等级:
    • 排名:千里之外
    • 原创:33篇
    • 转载:154篇
    • 译文:0篇
    • 评论:3条
    文章分类
    最新评论