更深层次的理解Context

来源:互联网 发布:mac找不到管理员账户 编辑:程序博客网 时间:2024/04/30 08:41

Context在开发Android应用的过程中扮演着非常重要的角色,比如启动一个Activity需要使用context.startActivity方法,将一个xml文件转换为一个View对象也需要使用Context对象,可以这么说,离开了这个类,Android开发寸步难行,对于这样一个类,我们又对他了解多少呢。我就说说我的感受吧,在刚开始学习Android开发时,感觉使用Context的地方一直就是传入一个Activity对象,久而久之感觉只要是Context的地方就传入一个Activity就行了,那么我们现在就来详细的分析一下Context和Activity的关系吧!

在开始本文之前我们先放置一个问题在这里:

我们平时在获取项目资源时使用context.getResources()的时候为什么放回的是同一个值,明明是使用不同的Activity调用getResources返回结果却是一样的。



Context本身是一个纯的abstract类,ContextWrapper是对Context的一个包装而已,它的内部包含了一个Context对象,其实对ContextWrapper的方法调用最终都是调用其中的Context对象完成的,至于ContextThremeWrapper,很明显和Theme有关,所以Activity从ContextThemmWrapper继承,而Service从ContextWrapper继承,ContextImpl是唯一一个真正实现了Context中方法的类。

 

从上面的继承关系来看,每一个Activity就是一个Context,每一个Service就是一个Context,这也就是为什么使用Context的地方可以被Activity或者Service替换了。

 

根据前面所说,由于实现了Context的只有ContextImpl类,Activity和Service本没有真正的实现,他们只是内部包含了一个真实的Context对象而已,也就是在在创建Activity或者Service的时候肯定要创建爱你一个ContextImpl对象,并赋值到Activity中的Context类型变量中。那我们就来看看Andorid源码中有哪些地方创建了ContextImpl.

据统计Android中创建ContextImpl的地方一共有7处:

在PackageInfo.makeApplication()中

在performLaunchActivity()中

在handleCreateBackupAgent()中

在handleCreateService()中

2次在hanldBinderAppplication()中

在attach()方法中



由于创建ContextImpl的基本原理类似,所以这里只会分析几个比较有代表性的地方:

1、  Application对应的Context

在应用程序启动时,都会创建一个Application对象,所以辗转调用到handleBindApplication()方法。

[java] view plaincopyprint?
  1. private final void handleBindApplication(AppBindData data) {  
  2.        mBoundApplication = data;  
  3.        mConfiguration = new Configuration(data.config);  
  4.   
  5.        ....  
  6.        data.info = getPackageInfoNoCheck(data.appInfo);  
  7.   
  8.             ...  
  9.          
  10.        Application app = data.info.makeApplication(data.restrictedBackupMode, null);  
  11.        mInitialApplication = app;  
  12.   
  13.      ....  
  14.   }  

其中data.info是LoadedApk类型的,到getPackageInfoNoCheck中看看源码

[java] view plaincopyprint?
  1. public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai) {  
  2.         return getPackageInfo(ai, nullfalsetrue);  
  3. }  

里面其实调用的是getPackageInfo,继续跟进:

[java] view plaincopyprint?
  1. if (includeCode) {  
  2.                 ref = mPackages.get(aInfo.packageName);  
  3.             } else {  
  4.                 ref = mResourcePackages.get(aInfo.packageName);  
  5.             }  
  6.             LoadedApk packageInfo = ref != null ? ref.get() : null;  
  7.             if (packageInfo == null || (packageInfo.mResources != null  
  8.                     && !packageInfo.mResources.getAssets().isUpToDate())) {  
  9.                 if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "  
  10.                         : "Loading resource-only package ") + aInfo.packageName  
  11.                         + " (in " + (mBoundApplication != null  
  12.                                 ? mBoundApplication.processName : null)  
  13.                         + ")");  
  14.                 packageInfo =  
  15.                     new LoadedApk(this, aInfo, this, baseLoader,  
  16.                             securityViolation, includeCode &&  
  17.                             (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);  
  18. if (includeCode) {  
  19.                     mPackages.put(aInfo.packageName,  
  20.                             new WeakReference<LoadedApk>(packageInfo));  
  21.                 } else {  
  22.                     mResourcePackages.put(aInfo.packageName,  
  23.                             new WeakReference<LoadedApk>(packageInfo));  
  24.                 }  

由于includeCode传入的是true,所以首先从mPackages中获取,如果没有,则new一个出来,并放入mPackages里面去,注意,这里的mPackages是ActivityThread中的属性。

下面继续分析一下LoadedApk这个类中的makeApplication函数

[java] view plaincopyprint?
  1. try {  
  2.             java.lang.ClassLoader cl = getClassLoader();  
  3.             //创建一个ContextImpl对象  
  4.             ContextImpl appContext = new ContextImpl();  
  5.             appContext.init(thisnull, mActivityThread);  
  6.             app = mActivityThread.mInstrumentation.newApplication(  
  7.                     cl, appClass, appContext);  
  8.             appContext.setOuterContext(app);  
  9.         } catch (Exception e) {  
  10.             if (!mActivityThread.mInstrumentation.onException(app, e)) {  
  11.                 throw new RuntimeException(  
  12.                     "Unable to instantiate application " + appClass  
  13.                     + ": " + e.toString(), e);  
  14.             }  
  15.         }  
  16. 这里创建了一个ContextImpl对象,并调用了它的init方法,现在进入init方法。  
  17. mPackageInfo = packageInfo;  
  18. mResources = mPackageInfo.getResources(mainThread);  

对mPackageInof和mResources两个变量初始化

回到makeApplication中,创建了一个Application对象,并将appContext传进去,其实就是将appContext传递给ContextWrapper中的Context类型变量(Application也是继承ContextWrapper)

2、Activity中的Context

在创建一个Activity时,经过辗转调用,会执行handleLaunchActivity(),然后调用performLaunchActivity(),该方法创建ContextImpl代码如下:

r.packageInfo= getPackageInfo(aInfo.applicationInfo,

                   Context.CONTEXT_INCLUDE_CODE);

 

ContextImplappContext = new ContextImpl();

                appContext.init(r.packageInfo,r.token, this);

               appContext.setOuterContext(activity);

 

activity.attach(appContext,this, getInstrumentation(), r.token,

                        r.ident, app, r.intent,r.activityInfo, title, r.parent,

                        r.embeddedID,r.lastNonConfigurationInstance,

                       r.lastNonConfigurationChildInstances, config);

由于getPackageInfo函数之前已经分析过了,稍微有点区别,但是大致流程是差不多的,所以此处的appContext执行init之后,其中的mPackages变量和mResources变量时一样的,activity通过attach函数将该appContext赋值到ContextWrapper中的Context类型变量


3、Service中的Context

同样 在创建一个Service时,经过辗转调用会调用到scheduleCreateService方法,之后会巧用handleCreateService

LoadedApkpackageInfo = getPackageInfoNoCheck(

               data.info.applicationInfo);

 

ContextImplcontext = new ContextImpl();

            context.init(packageInfo, null,this);

 

            Application app =packageInfo.makeApplication(false, mInstrumentation);

            context.setOuterContext(service);

            service.attach(context, this,data.info.name, data.token, app,

                   ActivityManagerNative.getDefault());

其思路和上面两个基本一样,在此就不再详述。

在此总结一下:
(1)Context是一个抽象类,ContextWrapper是对Context的封装,它包含一个Context类型的变量,ContextWrapper的功能函数内部其实都是调用里面的Context类型变量完成的。Application,Service,Activity等都是直接或者间接继承自ContextWrapper,但是并没有真正的实现其中的功能,Application,Service,Activity中关于Context的功能都是通过其内部的Context类型变量完成的,而这个变量的真实对象必定是ContextImpl,所以没创建一个Application,Activity,Servcice便会创建一个ContextImpl,并且这些ContextImpl中的mPackages和mResources变量都是一样的,所以不管使用Acitivty还是Service调用getResources得到相同的结果

(2)在一个apk中,Context的数量等于Activity个数+Service个数+1.


0 0
原创粉丝点击