• 一些Context对资源访问的细节

    不同的Context得到的是同一份资源
    服务器君一共花费 84.817 ms 进行了 4 次数据库查询,努力地为您提供了这个页面。
    广告很萌的

    很明确,不同的Context得到的都是同一份资源,看下面的分析。

    得到资源的方式为context.getResources,而真正的实现位于ContextImpl中的getResources方法,在ContextImpl中有一个成员 private Resources mResources,它就是getResources方法返回的结果,mResources的赋值代码为:

    mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
    	Display.DEFAULT_DISPLAY, null, compatInfo, activityToken);
    

    下面看一下ResourcesManager的getTopLevelResources方法,这个方法的思想是这样的:在ResourcesManager中,所有的资源对象都被存储在ArrayMap中,首先根据当前的请求参数去查找资源,如果找到了就返回,否则就创建一个资源对象放到ArrayMap中。

    有一点需要说明的是为什么会有多个资源对象,原因很简单,因为res下可能存在多个适配不同设备、不同分辨率、不同系统版本的目录,按照android系统的设计,不同设备在访问同一个应用的时候访问的资源可以不同,比如drawable-hdpi和drawable-xhdpi就是典型的例子。

    public Resources getTopLevelResources(String resDir, int displayId,  
            Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {  
        final float scale = compatInfo.applicationScale;  
        ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale,  
                token);  
        Resources r;  
        synchronized (this) {  
            // Resources is app scale dependent.  
            if (false) {  
                Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);  
            }  
            WeakReference<Resources> wr = mActiveResources.get(key);  
            r = wr != null ? wr.get() : null;  
            //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());  
            if (r != null && r.getAssets().isUpToDate()) {  
                if (false) {  
                    Slog.w(TAG, "Returning cached resources " + r + " " + resDir  
                            + ": appScale=" + r.getCompatibilityInfo().applicationScale);  
                }  
                return r;  
            }  
        }  
      
        //if (r != null) {  
        //    Slog.w(TAG, "Throwing away out-of-date resources!!!! "  
        //            + r + " " + resDir);  
        //}  
      
        AssetManager assets = new AssetManager();  
        if (assets.addAssetPath(resDir) == 0) {  
            return null;  
        }  
      
        //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);  
        DisplayMetrics dm = getDisplayMetricsLocked(displayId);  
        Configuration config;  
        boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);  
        final boolean hasOverrideConfig = key.hasOverrideConfiguration();  
        if (!isDefaultDisplay || hasOverrideConfig) {  
            config = new Configuration(getConfiguration());  
            if (!isDefaultDisplay) {  
                applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);  
            }  
            if (hasOverrideConfig) {  
                config.updateFrom(key.mOverrideConfiguration);  
            }  
        } else {  
            config = getConfiguration();  
        }  
        r = new Resources(assets, dm, config, compatInfo, token);  
        if (false) {  
            Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "  
                    + r.getConfiguration() + " appScale="  
                    + r.getCompatibilityInfo().applicationScale);  
        }  
      
        synchronized (this) {  
            WeakReference<Resources> wr = mActiveResources.get(key);  
            Resources existing = wr != null ? wr.get() : null;  
            if (existing != null && existing.getAssets().isUpToDate()) {  
                // Someone else already created the resources while we were  
                // unlocked; go ahead and use theirs.  
                r.getAssets().close();  
                return existing;  
            }  
      
            // XXX need to remove entries when weak references go away  
            mActiveResources.put(key, new WeakReference<Resources>(r));  
            return r;  
        }  
    }  
    

    根据上述代码中资源的请求机制,再加上ResourcesManager采用单例模式,这样就保证了不同的ContextImpl访问的是同一套资源,注意,这里说的同一套资源未必是同一个资源,因为资源可能位于不同的目录,但它一定是我们的应用的资源,或许这样来描述更准确,在设备参数和显示参数不变的情况下,不同的ContextImpl访问到的是同一份资源。设备参数不变是指手机的屏幕和android版本不变,显示参数不变是指手机的分辨率和横竖屏状态。也就是说,尽管Application、Activity、Service都有自己的ContextImpl,并且每个ContextImpl都有自己的mResources成员,但是由于它们的mResources成员都来自于唯一的ResourcesManager实例,所以它们看似不同的mResources其实都指向的是同一块内存(C语言的概念),因此,它们的mResources都是同一个对象(在设备参数和显示参数不变的情况下)。在横竖屏切换的情况下且应用中为横竖屏状态提供了不同的资源,处在横屏状态下的ContextImpl和处在竖屏状态下的ContextImpl访问的资源不是同一个资源对象。

    代码:单例模式的ResourcesManager类

    public static ResourcesManager getInstance() {  
        synchronized (ResourcesManager.class) {  
            if (sResourcesManager == null) {  
                sResourcesManager = new ResourcesManager();  
            }  
            return sResourcesManager;  
        }  
    } 
    

    getApplication和getApplicationContext的区别

    getApplication返回结果为Application,且不同的Activity和Service返回的Application均为同一个全局对象,在ActivityThread内部有一个列表专门用于维护所有应用的application

    inal ArrayList<Application> mAllApplications  = new ArrayList<Application>();
    

    getApplicationContext返回的也是Application对象,只不过返回类型为Context,看看它的实现

    @Override  
    public Context getApplicationContext() {  
        return (mPackageInfo != null) ?  
    		mPackageInfo.getApplication() : mMainThread.getApplication();  
    }  
    

    上面代码中mPackageInfo是包含当前应用的包信息、比如包名、应用的安装目录等,原则上来说,作为第三方应用,包信息mPackageInfo不可能为空,在这种情况下,getApplicationContext返回的对象和getApplication是同一个。但是对于系统应用,包信息有可能为空,具体就不深入研究了。从这种角度来说,对于第三方应用,一个应用只存在一个Application对象,且通过getApplication和getApplicationContext得到的是同一个对象,两者的区别仅仅是返回类型不同。

    顺带一提,关于应用中Context的数量问题,一个应用中Context的数量等于Activity的个数 + Service的个数 + 1,这个1为Application。

更多 推荐条目

Welcome to NowaMagic Academy!

现代魔法 推荐于 2013-02-27 10:23   

本章最新发布
随机专题
  1. [数据库技术] SQL基础语法 1 个条目
  2. [移动开发] 从代码角度去认识 Activity 4 个条目
  3. [Python程序设计] Django后台管理系统 2 个条目
  4. [PHP程序设计] PHP数组探索 4 个条目
  5. [智力开发与知识管理] 超越整体性学习 5 个条目
  6. [Python程序设计] Tornado表单处理 3 个条目
  7. [运维管理] 路由器与交换机 4 个条目
  8. [移动开发] Android里的ContentValues 2 个条目
  9. [移动开发] 简单了解Android Fragment 3 个条目
  10. [Python程序设计] Python HTTP服务器 7 个条目
  11. [Python程序设计] Django 入门知识浅介 10 个条目
  12. [智力开发与知识管理] 学习编程为什么没会这么难? 7 个条目
窗口 -- [博客]