ResourcesManager资源管理

1.Resources的分类

Resources可以分为Activity类型的和WindowContext类型两种类型的Resources,(ps:Application类型的Resources还待研究),谷歌源码的解释如下:

ResourcesManager.java - OpenGrok cross reference for /frameworks/base/core/java/android/app/ResourcesManager.java

298      /**
299       * Contains a resource derived from an {@link Activity} or {@link WindowContext} and information
300       * about how this resource expects its configuration to differ from the token's.
301       *
302       * @see ActivityResources
303       */
304      // TODO: Ideally this class should be called something token related, like TokenBasedResource.
305      private static class ActivityResource {
306          /**
307           * The override configuration applied on top of the token's override config for this
308           * resource.
309           */
310          public final Configuration overrideConfig = new Configuration();
311  
312          /**
313           * If non-null this resource expects its configuration to override the display from the
314           * token's configuration.
315           *
316           * @see #applyDisplayMetricsToConfiguration(DisplayMetrics, Configuration)
317           */
318          @Nullable
319          public Integer overrideDisplayId;
320  
321          @Nullable
322          public WeakReference<Resources> resources;
323  
324          private ActivityResource() {}
325      }

2 Activity类型的Resources创建过程:

2.1 在Resources创建之前,先理解下Activity和Resources之间的关系。

应用开发是通过Context.getResources()方法获取Resources对象,接下来我们看下Context对象是如何获取到Resources对象的。

一般情况下系统的Context对象有Activity、Application、Service类型的Context对象,其最终实现类是ContextImpl,因为在Activity的内部在attach的时候会传入一个Context对象,这个Context对象的实现类就是ContextImpl。具体的堆栈信息如下:

 performLaunchActivity(

因为ContextImpl类中持有Resources对象mResources,其调用setResources方法设置进来的,调用堆栈如下:

所以我们就清楚了Activity、ContextImpl、Resources、ResourcesImpl之间的关系如下:

2.2 Resources创建过程:

Activity类型的Resources是通过ResourcesManager的createResourcesForActivityLocked方法创建的:

ResourcesManager.java - OpenGrok cross reference for /frameworks/base/core/java/android/app/ResourcesManager.java

869      @NonNull
870      private Resources createResourcesForActivityLocked(@NonNull IBinder activityToken,
871              @NonNull Configuration initialOverrideConfig, @Nullable Integer overrideDisplayId,
872              @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
873              @NonNull CompatibilityInfo compatInfo) {
874          final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
875                  activityToken);
876          cleanupReferences(activityResources.activityResources,
877                  activityResources.activityResourcesQueue,
878                  (r) -> r.resources);
879  
880          Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
881                  : new Resources(classLoader);
882          resources.setImpl(impl);
883          resources.setCallbacks(mUpdateCallbacks);
884  
885          ActivityResource activityResource = new ActivityResource();
886          activityResource.resources = new WeakReference<>(resources,
887                  activityResources.activityResourcesQueue);
888          activityResource.overrideConfig.setTo(initialOverrideConfig);
889          activityResource.overrideDisplayId = overrideDisplayId;
890          activityResources.activityResources.add(activityResource);
891          if (DEBUG) {
892              Slog.d(TAG, "- creating new ref=" + resources);
893              Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
894          }
895          return resources;
896      }

在创建Resources对象的时候创建了ActivityResources对象,ActivityResources对象是个什么,它的作用是什么:

ResourcesManager.java - OpenGrok cross reference for /frameworks/base/core/java/android/app/ResourcesManager.java

256      /**
257       * Class containing the base configuration override and set of resources associated with an
258       * {@link Activity} or a {@link WindowContext}.
259       */
260      private static class ActivityResources {
261          /**
262           * Override config to apply to all resources associated with the token this instance is
263           * based on.
264           *
265           * @see #activityResources
266           * @see #getResources(IBinder, String, String[], String[], String[], String[], Integer,
267           * Configuration, CompatibilityInfo, ClassLoader, List)
268           */
269          public final Configuration overrideConfig = new Configuration();
270  
271          /**
272           * The display to apply to all resources associated with the token this instance is based
273           * on.
274           */
275          public int overrideDisplayId;
276  
277          /** List of {@link ActivityResource} associated with the token this instance is based on. */
278          public final ArrayList<ActivityResource> activityResources = new ArrayList<>();
279  
280          public final ReferenceQueue<Resources> activityResourcesQueue = new ReferenceQueue<>();
281  
282          @UnsupportedAppUsage
283          private ActivityResources() {}
284  
285          /** Returns the number of live resource references within {@code activityResources}. */
286          public int countLiveReferences() {
287              int count = 0;
288              for (int i = 0; i < activityResources.size(); i++) {
289                  WeakReference<Resources> resources = activityResources.get(i).resources;
290                  if (resources != null && resources.get() != null) {
291                      count++;
292                  }
293              }
294              return count;
295          }
296      }

我们通过注释可以看出ActivityResources就是一个它是一个包含了一个overrideConfig的一组Activity类型/WindowContext类型的集合,其成员activityResources集合包含了一组的ActivityResource,那么ActivityResource又是个什么:

ResourcesManager.java - OpenGrok cross reference for /frameworks/base/core/java/android/app/ResourcesManager.java

298      /**
299       * Contains a resource derived from an {@link Activity} or {@link WindowContext} and information
300       * about how this resource expects its configuration to differ from the token's.
301       *
302       * @see ActivityResources
303       */
304      // TODO: Ideally this class should be called something token related, like TokenBasedResource.
305      private static class ActivityResource {
306          /**
307           * The override configuration applied on top of the token's override config for this
308           * resource.
309           */
310          public final Configuration overrideConfig = new Configuration();
311  
312          /**
313           * If non-null this resource expects its configuration to override the display from the
314           * token's configuration.
315           *
316           * @see #applyDisplayMetricsToConfiguration(DisplayMetrics, Configuration)
317           */
318          @Nullable
319          public Integer overrideDisplayId;
320  
321          @Nullable
322          public WeakReference<Resources> resources;
323  
324          private ActivityResource() {}
325      }

ActivityResource通过成员变量resources来管理Resources的,下面整理一下Activity、ActivityClientRecord、ActivityResources、ActivityResource之间的关系:

 ResourcesManager中的mActivityResourceReferences成员存入的key是IBinder对象,其实就是一个activityToken对象,其是在LaunchActivityItem时由system_server的ATMS生成的,可以理解成通行证,用于标记Activity的身份,在ATMS侧可以找到ActivityRecord,在APP侧可以找到对应的ActivityClientRecord。从上图的对应关系我们可以知道,一个Activity可能会对应多个Resources对象,这是为什么呢,这就和Resources的缓存机制有关了,假如说我们的系统语言为中文,然后我们从中文切换到了英语,这个时候系统就会为我们创建一个和此次Configuration有关的新的Resources与之对应,然后我们将英语又切回到了中文,这个时候系统就会复用第一次创建的和中文有关的Resources对象。

3.Resources更新过程

一个Activity在初始化的时候会调用attach方法绑定一个ContextImpl对象,这个ContextImpl对象会一直到该页面销毁,一直保持不变,上面我们知道一个ContextImpl对象中会持有一个Resources对象,当我们切换语言的时候页面的时候ContextImpl是没有变的,要保证app资源的正确性,这个时候就要替换掉ContextImpl对象里面的Resources对象,这也就是一个Activity可能有多个Resources的原因。系统会把我们之前切换掉的Resources保存在ResourceManager中防止下一次又切换到相同的Configuration的时候重新创建Resources对象。ResourceManger主要就是通过下面这两个集合进行Resources复用的:

ResourcesManager.java - OpenGrok cross reference for /frameworks/base/core/java/android/app/ResourcesManager.java

108      /**
109       * A mapping of ResourceImpls and their configurations. These are heavy weight objects
110       * which should be reused as much as possible.
111       */
112      @UnsupportedAppUsage
113      private final ArrayMap<ResourcesKey, WeakReference<ResourcesImpl>> mResourceImpls =
114              new ArrayMap<>();
115  
116      /**
117       * A list of Resource references that can be reused.
118       */
119      @UnsupportedAppUsage
120      private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();

从上面的注释可以看出ResourcesManager保存了和Configuration有关的ResourcesKey和与之对应的ResourcesImpl对象。当我们下次再切换到相同的语言,系统创建的ResourceKey就和上一次切换的ResourceKey两个不同的对象能保证他们的mHash值保持一致,也就能顺利的从mResourceImpls当中取出缓存对象。下面摘录了ResourcesKey的mHash的计算方法:

ResourcesManager.java - OpenGrok cross reference for /frameworks/base/core/java/android/app/ResourcesManager.java

31  public final class ResourcesKey {
68      public ResourcesKey(@Nullable String resDir,
69                          @Nullable String[] splitResDirs,
70                          @Nullable String[] overlayPaths,
71                          @Nullable String[] libDirs,
72                          int overrideDisplayId,
73                          @Nullable Configuration overrideConfig,
74                          @Nullable CompatibilityInfo compatInfo,
75                          @Nullable ResourcesLoader[] loader) {
76          mResDir = resDir;
77          mSplitResDirs = splitResDirs;
78          mOverlayPaths = overlayPaths;
79          mLibDirs = libDirs;
80          mLoaders = (loader != null && loader.length == 0) ? null : loader;
81          mDisplayId = overrideDisplayId;
82          mOverrideConfiguration = new Configuration(overrideConfig != null
83                  ? overrideConfig : Configuration.EMPTY);
84          mCompatInfo = compatInfo != null ? compatInfo : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
85  
86          int hash = 17;
87          hash = 31 * hash + Objects.hashCode(mResDir);
88          hash = 31 * hash + Arrays.hashCode(mSplitResDirs);
89          hash = 31 * hash + Arrays.hashCode(mOverlayPaths);
90          hash = 31 * hash + Arrays.hashCode(mLibDirs);
91          hash = 31 * hash + Objects.hashCode(mDisplayId);
92          hash = 31 * hash + Objects.hashCode(mOverrideConfiguration);
93          hash = 31 * hash + Objects.hashCode(mCompatInfo);
94          hash = 31 * hash + Arrays.hashCode(mLoaders);
95          mHash = hash;
96      }
139      @Override
140      public boolean equals(@Nullable Object obj) {
141          if (!(obj instanceof ResourcesKey)) {
142              return false;
143          }
144  
145          ResourcesKey peer = (ResourcesKey) obj;
146          if (mHash != peer.mHash) {
147              // If the hashes don't match, the objects can't match.
148              return false;
149          }
150  
151          if (!Objects.equals(mResDir, peer.mResDir)) {
152              return false;
153          }
154          if (!Arrays.equals(mSplitResDirs, peer.mSplitResDirs)) {
155              return false;
156          }
157          if (!Arrays.equals(mOverlayPaths, peer.mOverlayPaths)) {
158              return false;
159          }
160          if (!Arrays.equals(mLibDirs, peer.mLibDirs)) {
161              return false;
162          }
163          if (mDisplayId != peer.mDisplayId) {
164              return false;
165          }
166          if (!Objects.equals(mOverrideConfiguration, peer.mOverrideConfiguration)) {
167              return false;
168          }
169          if (!Objects.equals(mCompatInfo, peer.mCompatInfo)) {
170              return false;
171          }
172          if (!Arrays.equals(mLoaders, peer.mLoaders)) {
173              return false;
174          }
175          return true;
176      }
209  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值