[关闭]
@act262 2017-06-01T08:52:07.000000Z 字数 7016 阅读 1232

Drawable资源的加载流程

Android_Drawable


API24+开始对Drawable的内部加载流程有所变动,但在Resources的调用表现接口不变。


以下基于API 25的SDK源码分析

基本流程如下:

Resources#getDrawable -> ResourcesImpl#loadDrawable -> ResourcesImpl#loadDrawableForCookie -> Drawable#createFromXml
-> Drawable#createFromXmlInner -> Resources#getDrawableInflater -> DrawableInflater#inflateFromXml

ResourcesImpl#loadDrawable

  1. Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme, boolean useCache) throws NotFoundException {
  2. try {
  3. if (TRACE_FOR_PRELOAD) {
  4. // Log only framework resources
  5. if ((id >>> 24) == 0x1) {
  6. final String name = getResourceName(id);
  7. if (name != null) {
  8. Log.d("PreloadDrawable", name);
  9. }
  10. }
  11. }
  12. final boolean isColorDrawable;
  13. final DrawableCache caches;
  14. final long key;
  15. // 区分普通Drawable还是ColorDrawable
  16. if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
  17. && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
  18. isColorDrawable = true;
  19. caches = mColorDrawableCache;
  20. key = value.data;
  21. } else {
  22. isColorDrawable = false;
  23. caches = mDrawableCache;
  24. key = (((long) value.assetCookie) << 32) | value.data;
  25. }
  26. // First, check whether we have a cached version of this drawable
  27. // that was inflated against the specified theme. Skip the cache if
  28. // we're currently preloading or we're not using the cache.
  29. if (!mPreloading && useCache) {
  30. final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
  31. if (cachedDrawable != null) {
  32. return cachedDrawable;
  33. }
  34. }
  35. // Next, check preloaded drawables. Preloaded drawables may contain
  36. // unresolved theme attributes.
  37. // 从预加载的缓存中获取ConstantState
  38. final Drawable.ConstantState cs;
  39. if (isColorDrawable) {
  40. cs = sPreloadedColorDrawables.get(key);
  41. } else {
  42. cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
  43. }
  44. // 使用缓存已有的ConstantState new出Drawable,复用ConstantState
  45. Drawable dr;
  46. if (cs != null) {
  47. dr = cs.newDrawable(wrapper);
  48. } else if (isColorDrawable) {
  49. dr = new ColorDrawable(value.data);
  50. } else {
  51. // 缓存没有找到则是0开始加载进来
  52. dr = loadDrawableForCookie(wrapper, value, id, null);
  53. }
  54. // Determine if the drawable has unresolved theme attributes. If it
  55. // does, we'll need to apply a theme and store it in a theme-specific
  56. // cache.
  57. final boolean canApplyTheme = dr != null && dr.canApplyTheme();
  58. if (canApplyTheme && theme != null) {
  59. dr = dr.mutate();
  60. dr.applyTheme(theme);
  61. dr.clearMutated();
  62. }
  63. // If we were able to obtain a drawable, store it in the appropriate
  64. // cache: preload, not themed, null theme, or theme-specific. Don't
  65. // pollute the cache with drawables loaded from a foreign density.
  66. if (dr != null && useCache) {
  67. // 缓存起来
  68. dr.setChangingConfigurations(value.changingConfigurations);
  69. cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
  70. }
  71. return dr;
  72. } catch (Exception e) {
  73. String name;
  74. try {
  75. name = getResourceName(id);
  76. } catch (NotFoundException e2) {
  77. name = "(missing name)";
  78. }
  79. // The target drawable might fail to load for any number of
  80. // reasons, but we always want to include the resource name.
  81. // Since the client already expects this method to throw a
  82. // NotFoundException, just throw one of those.
  83. final NotFoundException nfe = new NotFoundException("Drawable " + name
  84. + " with resource ID #0x" + Integer.toHexString(id), e);
  85. nfe.setStackTrace(new StackTraceElement[0]);
  86. throw nfe;
  87. }
  88. }

ResourcesImpl#loadDrawableForCookie

  1. private Drawable loadDrawableForCookie(Resources wrapper, TypedValue value, int id,
  2. Resources.Theme theme) {
  3. if (value.string == null) {
  4. throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
  5. + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
  6. }
  7. final String file = value.string.toString();
  8. if (TRACE_FOR_MISS_PRELOAD) {
  9. // Log only framework resources
  10. if ((id >>> 24) == 0x1) {
  11. final String name = getResourceName(id);
  12. if (name != null) {
  13. Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id)
  14. + ": " + name + " at " + file);
  15. }
  16. }
  17. }
  18. if (DEBUG_LOAD) {
  19. Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
  20. }
  21. final Drawable dr;
  22. Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
  23. try {
  24. if (file.endsWith(".xml")) {
  25. final XmlResourceParser rp = loadXmlResourceParser(
  26. file, id, value.assetCookie, "drawable");
  27. dr = Drawable.createFromXml(wrapper, rp, theme);
  28. rp.close();
  29. } else {
  30. final InputStream is = mAssets.openNonAsset(
  31. value.assetCookie, file, AssetManager.ACCESS_STREAMING);
  32. dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
  33. is.close();
  34. }
  35. } catch (Exception e) {
  36. Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
  37. final NotFoundException rnf = new NotFoundException(
  38. "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
  39. rnf.initCause(e);
  40. throw rnf;
  41. }
  42. Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
  43. return dr;
  44. }

不从缓存获取的情况下,也就是首次获取走的是全新的解析流程

DrawableInflater.javaDrawable解析

  1. public Drawable inflateFromXml(@NonNull String name, @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
  2. throws XmlPullParserException, IOException {
  3. // Inner classes must be referenced as Outer$Inner, but XML tag names
  4. // can't contain $, so the <drawable> tag allows developers to specify
  5. // the class in an attribute. We'll still run it through inflateFromTag
  6. // to stay consistent with how LayoutInflater works.
  7. // 解析自定义Drawable
  8. if (name.equals("drawable")) {
  9. name = attrs.getAttributeValue(null, "class");
  10. if (name == null) {
  11. throw new InflateException("<drawable> tag must specify class attribute");
  12. }
  13. }
  14. // 解析系统定义的drawable类
  15. Drawable drawable = inflateFromTag(name);
  16. if (drawable == null) {
  17. // 没有则解析自定义的Drawable
  18. drawable = inflateFromClass(name);
  19. }
  20. // inflate具体子类的实现,解析Drawable其他的属性
  21. drawable.inflate(mRes, parser, attrs, theme);
  22. return drawable;
  23. }
  24. // 这里解析的对应系统的各类Drawable
  25. private Drawable inflateFromTag(@NonNull String name) {
  26. switch (name) {
  27. case "selector":
  28. return new StateListDrawable();
  29. case "animated-selector":
  30. return new AnimatedStateListDrawable();
  31. case "level-list":
  32. return new LevelListDrawable();
  33. case "layer-list":
  34. return new LayerDrawable();
  35. case "transition":
  36. return new TransitionDrawable();
  37. case "ripple":
  38. return new RippleDrawable();
  39. case "color":
  40. return new ColorDrawable();
  41. case "shape":
  42. return new GradientDrawable();
  43. case "vector":
  44. return new VectorDrawable();
  45. case "animated-vector":
  46. return new AnimatedVectorDrawable();
  47. case "scale":
  48. return new ScaleDrawable();
  49. case "clip":
  50. return new ClipDrawable();
  51. case "rotate":
  52. return new RotateDrawable();
  53. case "animated-rotate":
  54. return new AnimatedRotateDrawable();
  55. case "animation-list":
  56. return new AnimationDrawable();
  57. case "inset":
  58. return new InsetDrawable();
  59. case "bitmap":
  60. return new BitmapDrawable();
  61. case "nine-patch":
  62. return new NinePatchDrawable();
  63. default:
  64. return null;
  65. }
  66. }
  67. // 这里解析的是自定义的Drawable类,自API24+以后可以在xml文件中使用自定义的Drawable类
  68. private Drawable inflateFromClass(@NonNull String className) {
  69. try {
  70. Constructor<? extends Drawable> constructor;
  71. synchronized (CONSTRUCTOR_MAP) {
  72. constructor = CONSTRUCTOR_MAP.get(className);
  73. if (constructor == null) {
  74. final Class<? extends Drawable> clazz =
  75. mClassLoader.loadClass(className).asSubclass(Drawable.class);
  76. constructor = clazz.getConstructor();
  77. CONSTRUCTOR_MAP.put(className, constructor);
  78. }
  79. }
  80. return constructor.newInstance();
  81. } catch (NoSuchMethodException e) {
  82. final InflateException ie = new InflateException(
  83. "Error inflating class " + className);
  84. ie.initCause(e);
  85. throw ie;
  86. } catch (ClassCastException e) {
  87. // If loaded class is not a Drawable subclass.
  88. final InflateException ie = new InflateException(
  89. "Class is not a Drawable " + className);
  90. ie.initCause(e);
  91. throw ie;
  92. } catch (ClassNotFoundException e) {
  93. // If loadClass fails, we should propagate the exception.
  94. final InflateException ie = new InflateException(
  95. "Class not found " + className);
  96. ie.initCause(e);
  97. throw ie;
  98. } catch (Exception e) {
  99. final InflateException ie = new InflateException(
  100. "Error inflating class " + className);
  101. ie.initCause(e);
  102. throw ie;
  103. }
  104. }
  1. 如果是系统自带的那些Drawable类,直接new出一个Drawable对象,到后面调用inflate将解析具体的每个实现类。
  2. 如果是我们自定义的Drawable,则用Class加载然后newInstancec出来,后面也可以通过inflate去解析其他的属性。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注