Android Appication及Activity的 Resource 生成代码分析

在“外销项目的小语种bug热修复方案”一文中,我们使用了替换mResource的方法来实现资源及字符串的热修复

之前在写这篇文章的时候 也一直在想一些问题

比如:

1:

为什么Application的getResource以及各个Activity的getResource获取的mResource对象不是同一个对象

2:

为什么 Activity的getResources().getAssert() 都是同一个Assert对象

而Appication的getResources().getAssert() 与Activity的Assert对象 又不是同一个

先看Application 的 Resource 的创建流程

// 以下代码来自ContextImpl
   
    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
                null);
        context.setResources(packageInfo.getResources());
        return context;
    }

继续往下看

以下代码来自LoadedApk   
 public Resources getResources() {
        if (mResources == null) {
            final String[] splitPaths;
            try {
                splitPaths = getSplitPaths(null);
            } catch (NameNotFoundException e) {
                // This should never fail.
                throw new AssertionError("null split not found");
            }

            mResources = ResourcesManager.getInstance().getResources(null, mResDir,
                    splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
                    Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
                    getClassLoader());
        }
        return mResources;
    }
以下代码来自ResourcesMananger
public @Nullable Resources getResources(@Nullable IBinder activityToken,
            @Nullable String resDir,
            @Nullable String[] splitResDirs,
            @Nullable String[] overlayDirs,
            @Nullable String[] libDirs,
            int displayId,
            @Nullable Configuration overrideConfig,
            @NonNull CompatibilityInfo compatInfo,
            @Nullable ClassLoader classLoader) {
        try {
            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesManager#getResources");
            final ResourcesKey key = new ResourcesKey(
                    resDir,
                    splitResDirs,
                    overlayDirs,
                    libDirs,
                    displayId,
                    overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
                    compatInfo);
            classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();

            //最终调用getOrCreateResources 
            return getOrCreateResources(activityToken, key, classLoader);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
    }
   private @Nullable Resources getOrCreateResources(@Nullable IBinder activityToken,
            @NonNull ResourcesKey key, @NonNull ClassLoader classLoader) {
        。。。。。。。。。。。。。。。。。。。。。。。。。。
        。。。。。。。。。。。。。。。。。。。。。。。。。。
        synchronized (this) {
            。。。。。。。。。。。。。。。。。。。。。。。

            //这边最终是拿到了一个resourcesImpl 生成了Resources 

            final Resources resources;

            //这边我的理解 如果是Application启动 activityToken 就是空的 如有错误请大神指正
            if (activityToken != null) {
                resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
                        resourcesImpl, key.mCompatInfo);
            } else {
                resources = getOrCreateResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
            }
            return resources;
        }
    }

看下最后两个生成Resource的方法


private @NonNull Resources getOrCreateResourcesForActivityLocked(@NonNull IBinder activityToken,
            @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
            @NonNull CompatibilityInfo compatInfo) {
        。。。。。。。。。。。。。。。。。。。。。。
        。。。。。。。。。。。。。。。。。。。。。。
        //这边最终是实例化了一个新的对象Resources
        Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
                : new Resources(classLoader);
        resources.setImpl(impl);
        activityResources.activityResources.add(new WeakReference<>(resources));
        if (DEBUG) {
            Slog.d(TAG, "- creating new ref=" + resources);
            Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
        }
        return resources;
    }

    private @NonNull Resources getOrCreateResourcesLocked(@NonNull ClassLoader classLoader,
            @NonNull ResourcesImpl impl, @NonNull CompatibilityInfo compatInfo) {
        。。。。。。。。。。。。。。。。。。。。。。。。
        // Create a new Resources reference and use the existing ResourcesImpl object.
        //跟刚刚分析的那个方法是一样的操作 实例化新的Resources
        Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
                : new Resources(classLoader);
        resources.setImpl(impl);
        mResourceReferences.add(new WeakReference<>(resources));
        if (DEBUG) {
            Slog.d(TAG, "- creating new ref=" + resources);
            Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
        }
        return resources;
    }

再来看下Activity的Resources生成过程

    static ContextImpl createActivityContext(ActivityThread mainThread,
            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
            Configuration overrideConfiguration) {
        。。。。。。。。。。。。。。。。。。。。。

        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
                activityToken, null, 0, classLoader);

        。。。。。。。。。。。。。。。。。。。。。。。

        final ResourcesManager resourcesManager = ResourcesManager.getInstance();

        // Create the base resources for which all configuration contexts for this Activity
        // will be rebased upon.
        context.setResources(resourcesManager.createBaseActivityResources(activityToken,
                packageInfo.getResDir(),
                splitDirs,
                packageInfo.getOverlayDirs(),
                packageInfo.getApplicationInfo().sharedLibraryFiles,
                displayId,
                overrideConfiguration,
                compatInfo,
                classLoader));
        context.mDisplay = resourcesManager.getAdjustedDisplay(displayId,
                context.getResources());
        return context;
    }
    public @Nullable Resources createBaseActivityResources(@NonNull IBinder activityToken,
            @Nullable String resDir,
            @Nullable String[] splitResDirs,
            @Nullable String[] overlayDirs,
            @Nullable String[] libDirs,
            int displayId,
            @Nullable Configuration overrideConfig,
            @NonNull CompatibilityInfo compatInfo,
            @Nullable ClassLoader classLoader) {
            
            .........................................

            // Update any existing Activity Resources references.
            
            //这里是重点
            updateResourcesForActivity(activityToken, overrideConfig, displayId,
                    false /* movedToDifferentDisplay */);

            // Now request an actual Resources object.
            return getOrCreateResources(activityToken, key, classLoader);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
    }
    public void updateResourcesForActivity(@NonNull IBinder activityToken,
            @Nullable Configuration overrideConfig, int displayId,
            boolean movedToDifferentDisplay) {
        try {
             。。。。。。。。。。。。。。。
            synchronized (this) {
                //取出缓存的ActivityResources 
                final ActivityResources activityResources =
                        getOrCreateActivityResourcesStructLocked(activityToken);
                if (Objects.equals(activityResources.overrideConfig, overrideConfig)
                        && !movedToDifferentDisplay) {
                    // 它们是相同的,没有显示ID的变化,没有工作要做。
                    return;
                }

                // Grab a copy of the old configuration so we can create the delta's of each
                // Resources object associated with this Activity.
                final Configuration oldConfig = new Configuration(activityResources.overrideConfig);

                // 更新缓存
                if (overrideConfig != null) {
                    activityResources.overrideConfig.setTo(overrideConfig);
                } else {
                    activityResources.overrideConfig.unset();
                }


                final boolean activityHasOverrideConfig =
                        !activityResources.overrideConfig.equals(Configuration.EMPTY);

                // 重新定位与此活动关联的每个资源。
                final int refCount = activityResources.activityResources.size();
                for (int i = 0; i < refCount; i++) {
                    WeakReference<Resources> weakResRef = activityResources.activityResources.get(
                            i);
                    Resources resources = weakResRef.get();
                    if (resources == null) {
                        continue;
                    }

                    // 提取上次用于为此活动创建资源的ResourcesKey。
                    final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
                    if (oldKey == null) {
                        Slog.e(TAG, "can't find ResourcesKey for resources impl="
                                + resources.getImpl());
                        continue;
                    }

                    // Build the new override configuration for this ResourcesKey.
                    final Configuration rebasedOverrideConfig = new Configuration();
                    if (overrideConfig != null) {
                        rebasedOverrideConfig.setTo(overrideConfig);
                    }

                    if (activityHasOverrideConfig && oldKey.hasOverrideConfiguration()) {
                        // Generate a delta between the old base Activity override configuration and
                        // the actual final override configuration that was used to figure out the
                        // real delta this Resources object wanted.
                        Configuration overrideOverrideConfig = Configuration.generateDelta(
                                oldConfig, oldKey.mOverrideConfiguration);
                        rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
                    }

                    // 根据数据创建了一个新的key
                    final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
                            oldKey.mSplitResDirs,
                            oldKey.mOverlayDirs, oldKey.mLibDirs, displayId,
                            rebasedOverrideConfig, oldKey.mCompatInfo);

                    //根据新的key从mResourceImpls里面去找
                    //这边有可能找不到 也有可能找到 因为ResourcesKey的equals和hashcode方法被改写了,部分数据匹配依然能找到
                    ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
                    if (resourcesImpl == null) {
                        resourcesImpl = createResourcesImpl(newKey);
                        if (resourcesImpl != null) {
                            mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
                        }
                    }
                    //找到了 就缓存起来了 
                    if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
                        // Set the ResourcesImpl, updating it for all users of this Resources
                        // object.
                        resources.setImpl(resourcesImpl);
                    }
                }
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        }
    }

看下ResourcesKey的hash生成规则

        int hash = 17;
        hash = 31 * hash + Objects.hashCode(mResDir);
        hash = 31 * hash + Arrays.hashCode(mSplitResDirs);
        hash = 31 * hash + Arrays.hashCode(mOverlayDirs);
        hash = 31 * hash + Arrays.hashCode(mLibDirs);
        hash = 31 * hash + mDisplayId;
        hash = 31 * hash + Objects.hashCode(mOverrideConfiguration);
        hash = 31 * hash + Objects.hashCode(mCompatInfo);
        mHash = hash;

现在我们可以回答之前提到的两个问题

一:为啥Resources对象都不相同

因为每次都是new出来的

二:为啥getAssert Activity的都相同

因为ResurcesImpl 是复用的 (一个Assert 对应一个ResurcesImpl )

复用逻辑设计ResourcesKey的比对,ResourcesKey比对的是资源路径,Configuration 等参数

ActivityToken 作为ActivityResources的缓存key 使用ActivityToken记录相同Token下的Activity使用的Resources

相同ActivityToken的Activity 也有可能因为config不同 导致Resources不同 因此ActivityResources里面记录了一个装满Resources的list

缓存设计逻辑:

1:先使用ActivityToken取相同token的ActivityResources

2:使用找到的ActivityResources取里面缓存的Resources 如果存在就使用它加上新的参数生成新的ResourcesKey

3:使用ResourcesKey去ResourcesImps这个缓存下面再去找资源

4:找不到就重新创建

三:为啥Application的Assert和Activity的不一样

因为Application在创建Resources的时候传入的configuration 是null  生成的ResourcesKey与Activity不一致

所以没有被Activity复用

以上分析 完全是根据源代码进行分析的,小弟不才,有些地方看的也是迷迷糊糊,如有说错的地方 麻烦帮忙指正

发布了24 篇原创文章 · 获赞 3 · 访问量 6143

猜你喜欢

转载自blog.csdn.net/binghelonglong123/article/details/88637334