Android Resource是如何获取颜色和图片的

首先扯点别的:上海天气真的是太热了。

Resource 获取颜色

我们使用的颜色通常有两种,一种是在 src/main/res/values/colors.xml 文件中定义的普通的16进制的颜色。

<color name="colorPrimary">#FF3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FFFF4081</color>

一种是在 src/main/res/color 目录下定义的xml格式的颜色文件。这种颜色是一个selector对象。例如: color_list.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:color="#FF4081"/>
    <item android:color="@color/colorPrimary" android:state_focused="true"/>
    <item android:color="@color/colorPrimaryDark" android:state_pressed="true"/>
</selector>

加载颜色,调用 Resources 的 getColor() 方法。

public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException {
	final TypedValue value = obtainTempTypedValue();
	try {
		final ResourcesImpl impl = mResourcesImpl;
		impl.getValue(id, value, true);
		//注释1处,获取正常的16进制颜色
		if (value.type >= TypedValue.TYPE_FIRST_iNT
		                    && value.type <= TypedValue.TYPE_LAST_iNT) {
			//返回颜色
			return value.data;
		} else if (value.type != TypedValue.TYPE_STRING) {
			throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
			                        + " type #0x" + Integer.toHexString(value.type) + " is not valid");
		}
		//注释2处,获取在src/main/res/color 目录下定义的xml格式的颜色
		final ColorStateList csl = impl.loadColorStateList(this, value, id, theme);
		return csl.getDefaultColor();
	}
	finally {
		releaseTempTypedValue(value);
	}
}

注释1处,获取正常的16进制颜色并返回。

注释2处,获取在src/main/res/color 目录下定义的xml格式的颜色。调用 ResourcesImpl 的 loadColorStateList 方法。

@NonNull
ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id, Resources.Theme theme)
throws NotFoundException {
  if (TRACE_FOR_PRELOAD) {
    // Log only framework resources
    if ((id >>> 24) == 0x1) {
      final String name = getResourceName(id);
      if (name != null) android.util.Log.d("PreloadColorStateList", name);
    }
  }

  final long key = (((long) value.assetCookie) << 32) | value.data;

  // Handle inline color definitions.
  if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
    return getColorStateListFromInt(value, key);
  }

  //注释1处,调用 loadComplexColorFromName 方法。 
  ComplexColor complexColor = loadComplexColorFromName(wrapper, theme, value, id);
  if (complexColor != null && complexColor instanceof ColorStateList) {
    //注释2处,返回获取的颜色值。
    return (ColorStateList) complexColor;
  }

  throw new NotFoundException("Can't find ColorStateList from drawable resource ID #0x" + Integer.toHexString(id));
}

注释1处,调用 loadComplexColorFromName 方法。

@Nullable
private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme, TypedValue value, int id) {
  final long key = (((long) value.assetCookie) << 32) | value.data;
  final ConfigurationBoundResourceCache < ComplexColor > cache = mComplexColorCache;
  ComplexColor complexColor = cache.getInstance(key, wrapper, theme);
  //先从缓存里获取。
  if (complexColor != null) {
    return complexColor;
  }

  final android.content.res.ConstantState < ComplexColor > factory = sPreloadedComplexColors.get(key);

  if (factory != null) {
    complexColor = factory.newInstance(wrapper, theme);
  }
  if (complexColor == null) {
    //注释1处,调用 loadComplexColorForCookie 方法。
    complexColor = loadComplexColorForCookie(wrapper, value, id, theme);
  }

  if (complexColor != null) {
    
    complexColor.setBaseChangingConfigurations(value.changingConfigurations);

    if (mPreloading) {
      if (verifyPreloadConfig(complexColor.getChangingConfigurations(), 0, value.resourceId, "color")) {
        sPreloadedComplexColors.put(key, complexColor.getConstantState());
      }
    } else {
      //注释2处,加入缓存
      cache.put(key, theme, complexColor.getConstantState());
    }
  }
  return complexColor;
}

注释1处,调用 loadComplexColorForCookie 方法。

@NonNull
private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id, Resources.Theme theme) {
    if(value.string == null) {
        throw new UnsupportedOperationException("Can't convert to ComplexColor: type=0x" + value.type);
    }
    //注释1处,在这个例子中就是 res/color/color_list.xml
    final String file = value.string.toString();
    if(TRACE_FOR_MISS_PRELOAD) {
        // Log only framework resources
        if((id >>> 24) == 0x1) {
            final String name = getResourceName(id);
            if(name != null) {
                Log.d(TAG, "Loading framework ComplexColor #" + Integer.toHexString(id) + ": " + name + " at " + file);
            }
        }
    }
    if(DEBUG_LOAD) {
        Log.v(TAG, "Loading ComplexColor for cookie " + value.assetCookie + ": " + file);
    }
    ComplexColor complexColor = null;
    Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
    //注释2处,在res/color/文件夹下定义的颜色必须是以xml文件
    if(file.endsWith(".xml")) {
        try {
            final XmlResourceParser parser = loadXmlResourceParser(file, id, value.assetCookie, "ComplexColor");
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            int type;
            while((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
                // Seek parser to start tag.
            }
            if(type != XmlPullParser.START_TAG) {
                throw new XmlPullParserException("No start tag found");
            }
            final String name = parser.getName();
            //注释3处,处理渐变色类型
            if(name.equals("gradient")) {
                complexColor = GradientColor.createFromXmlInner(wrapper, parser, attrs, theme);
            } else if(name.equals("selector")) {
                //注释4处,处理selector类型
                complexColor = ColorStateList.createFromXmlInner(wrapper, parser, attrs, theme);
            }
            parser.close();
        } catch(Exception e) {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
            final NotFoundException rnf = new NotFoundException("File " + file + " from ComplexColor resource ID #0x" + Integer.toHexString(id));
            rnf.initCause(e);
            throw rnf;
        }
    } else {
        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        throw new NotFoundException("File " + file + " from drawable resource ID #0x" + Integer.toHexString(id) + ": .xml extension required");
    }
    Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
    return complexColor;
}

注释1处,在这个例子中就是 res/color/color_list.xml。
注释2处,在res/color/文件夹下定义的颜色必须是以xml文件。
注释3处,处理渐变色类型。
注释4处,处理selector类型。

回到 loadComplexColorFromName 方法的 注释2处,将获取到的颜色加入缓存,下次就不用再次解析xml文件了。

回到 ResourcesImpl 的 loadColorStateList 方法的注释2处,返回获取的颜色值。

回到 Resources 的 getColor() 方法的注释2处,获取在src/main/res/color 目录下定义的xml格式的颜色,并且返回的是xml文件中默认状态的颜色。在这个例子中就是selector文件中的
<item android:color="#FF4081"/>

Resource获取图片

Resource 的 getDrawable 方法。

public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
throws NotFoundException {
    return getDrawableForDensity(id, 0, theme);
}

Resources 的 getDrawableForDensity 方法。

public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
	final TypedValue value = obtainTempTypedValue();
	try {
		final ResourcesImpl impl = mResourcesImpl;
		//注释1处,调用ResourcesImpl 的 getValueForDensity 方法。
		impl.getValueForDensity(id, density, value, true);
		//注释2处,调用ResourcesImpl 的loadDrawable 方法。
		return impl.loadDrawable(this, value, id, density, theme);
	}
	finally {
		releaseTempTypedValue(value);
	}
}

注释1处,调用ResourcesImpl 的 getValueForDensity 方法。

void getValueForDensity(@AnyRes int id, int density, TypedValue outValue,
            Boolean resolveRefs) throws NotFoundException {
	//注释1处,调用 AssetManager 的 getResourceValue 方法。
	Boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
	if (found) {
		return;
	}
	throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
}

注释1处,调用 AssetManager 的 getResourceValue 方法。这个方法大概就是返回资源在特定密度下的信息,返回的信息存储在 outValue 中,如果传入的 densityDpi 参数为0 ,就使用当前 Configuration 中的density。


final Boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
            Boolean resolveRefs) {
	synchronized (this) {
		//注释1处,调用 loadResourceValue 方法。 这是一个 native方法。
		final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
		if (block < 0) {
			return false;
		}
		// Convert the changing configurations flags populated by native code.
		outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
		                    outValue.changingConfigurations);
		if (outValue.type == TypedValue.TYPE_STRING) {
			outValue.string = mStringBlocks[block].get(outValue.data);
		}
		return true;
	}
}

Resources 的 getDrawableForDensity 方法的注释2处 ,调用ResourcesImpl 的loadDrawable 方法。

Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
            int density, @Nullable Resources.Theme theme)
            throws NotFoundException {
	// If the drawable's XML lives in our current density qualifier,
	// it's okay to use a scaled version from the cache. Otherwise, we
	// need to actually load the drawable from XML.
	//注释0处,在密度为0,或者传入的密度和当前的密度一样的时候,使用缓存。
	final Boolean useCache = density == 0 || value.density == mMetrics.densityDpi;
	// Pretend the requested density is actually the display density. If
	// the drawable returned is not the requested density, then force it
	// to be scaled later by dividing its density by the ratio of
	// requested density to actual device density. Drawables that have
	// undefined density or no density don't need to be handled here.
	if (density > 0 && value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
		if (value.density == density) {
			value.density = mMetrics.densityDpi;
		} else {
			value.density = (value.density * mMetrics.densityDpi) / density;
		}
	}
	try {
		//...
		//标记是否是 ColorDrawable 
		final Boolean isColorDrawable;
		//该主题下的绘制资源缓存
		final DrawableCache caches;
		final long key;
		//注释1处,条件满足则代表是 ColorDrawable ,我的理解 ColorDrawable 就是我们直接给 View 的 Background 设置一个颜色。 例如 android:background="@color/colorPrimary"
		if (value.type >= TypedValue.TYPE_FIRST_COLOR_iNT
		                    && value.type <= TypedValue.TYPE_LAST_COLOR_iNT) {

			isColorDrawable = true;
			caches = mColorDrawableCache;
			key = value.data;
		} else {
			isColorDrawable = false;
			caches = mDrawableCache;
			key = (((long) value.assetCookie) << 32) | value.data;
		}
		// First, check whether we have a cached version of this drawable
		// that was inflated against the specified theme. Skip the cache if
		// we're currently preloading or we're not using the cache.
		if (!mPreloading && useCache) {//条件为true
			final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
			if (cachedDrawable != null) {
				//注释2处,从缓存中获取的不为null,直接返回。
				cachedDrawable.setChangingConfigurations(value.changingConfigurations);
				return cachedDrawable;
			}
		}
		// Next, check preloaded drawables. Preloaded drawables may contain
		// unresolved theme attributes.
		//注释3处,从预加载的资源中获取,只有 zygote 进程才会预加载一些资源,这些资源是共享的。系统中的每个应用都可以使用。 
		final Drawable.ConstantState cs;
		if (isColorDrawable) {
			cs = sPreloadedColorDrawables.get(key);
		} else {
			cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
		}
		Drawable dr;
		Boolean needsNewDrawableAfterCache = false;
		if (cs != null) {
			if (TRACE_FOR_DETAILED_PRELOAD) {
				// Log only framework resources
				if (((id >>> 24) == 0x1) && (android.os.Process.myUid() != 0)) {
					final String name = getResourceName(id);
					//命中的预加载的资源
					if (name != null) {
						Log.d(TAG_PRELOAD, "Hit preloaded FW drawable #"
						                                    + Integer.toHexString(id) + " " + name);
					}
				}
			}
			//注释4处,从预加载的资源中获取Drawable
			dr = cs.newDrawable(wrapper);
		} else if (isColorDrawable) {
			//注释5处,创建新的 ColorDrawable
			dr = new ColorDrawable(value.data);
		} else {
			//注释6处,创建新的 Drawable 从 XML文件或者 资源流中加载 drawable
			dr = loadDrawableForCookie(wrapper, value, id, density);
		}
		// DrawableContainer' constant state has drawables instances. In order to leave the
		// constant state intact in the cache, we need to create a new DrawableContainer after
		// added to cache.
		if (dr instanceof DrawableContainer)  {
			needsNewDrawableAfterCache = true;
		}
		// Determine if the drawable has unresolved theme attributes. If it
		// does, we'll need to apply a theme and store it in a theme-specific
		// cache.
		//应用主题
		final Boolean canApplyTheme = dr != null && dr.canApplyTheme();
		if (canApplyTheme && theme != null) {
			dr = dr.mutate();
			dr.applyTheme(theme);
			dr.clearMutated();
		}
		// 如果我们能够获取一个 drawable,把它加入合适的缓存:预加载的缓存,没有主题的缓存,null主题的缓存,特定主题的缓存。
		if (dr != null) {
			dr.setChangingConfigurations(value.changingConfigurations);
			if (useCache) {
				//加入缓存
				cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
				if (needsNewDrawableAfterCache) {
					Drawable.ConstantState state = dr.getConstantState();
					if (state != null) {
						dr = state.newDrawable(wrapper);
					}
				}
			}
		}
		return dr;
	}
	catch (Exception e) {
		String name;
		try {
			name = getResourceName(id);
		}
		catch (NotFoundException e2) {
			name = "(missing name)";
		}
		// The target drawable might fail to load for any number of
		// reasons, but we always want to include the resource name.
		// Since the client already expects this method to throw a
		// NotFoundException, just throw one of those.
		final NotFoundException nfe = new NotFoundException("Drawable " + name
		                    + " with resource ID #0x" + Integer.toHexString(id), e);
		nfe.setStackTrace(new StackTraceElement[0]);
		throw nfe;
	}
}

注释0处,在密度为0,或者传入的密度和当前的密度一样的时候,使用缓存。

注释1处,条件满足则代表是 ColorDrawable ,我的理解 ColorDrawable 就是我们直接给 View 的 Background 设置一个颜色。 例如 android:background=“@color/colorPrimary” 。

注释2处,从缓存中获取的不为null,直接返回。

注释3处,从预加载的资源中获取,只有 zygote 进程才会预加载一些资源,这些资源是共享的。系统中的每个应用都可以使用。

注释4处,如果是预加载的资源,就使用预加载的资源获取Drawable。

注释5处,创建新的 ColorDrawable。

注释6处,创建新的 Drawable 从 XML文件或者 资源流中加载 drawable。

注释6处,创建新的 Drawable


/**
 * 从 XML文件或者 资源流中加载 drawable
 *
 * @return Drawable, or null if Drawable cannot be decoded.
 */
@Nullable
private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value, int id, int density) {
    if(value.string == null) {
        throw new NotFoundException("Resource "
            " + getResourceName(id) + "
            " (" + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
    }
    //注释0处, 类似 res/drawable-xxhdpi-v4/avatar.png
    final String file = value.string.toString();
    if(TRACE_FOR_MISS_PRELOAD) {
        // Log only framework resources
        if((id >>> 24) == 0x1) {
            final String name = getResourceName(id);
            if(name != null) {
                Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id) + ": " + name + " at " + file);
            }
        }
    }
    // For preload tracing.
    long startTime = 0;
    int startBitmapCount = 0;
    long startBitmapSize = 0;
    int startDrawableCount = 0;
    if(TRACE_FOR_DETAILED_PRELOAD) {
        startTime = System.nanoTime();
        startBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
        startBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
        startDrawableCount = sPreloadTracingNumLoadedDrawables;
    }
    if(DEBUG_LOAD) {
        Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
    }
    final Drawable dr;
    Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
    LookupStack stack = mLookupStack.get();
    try {
        // Perform a linear search to check if we have already referenced this resource before.
        if(stack.contains(id)) {
            throw new Exception("Recursive reference in drawable");
        }
        stack.push(id);
        try {
            //注释1处,从 xml 文件加载
            if(file.endsWith(".xml")) {
                final XmlResourceParser rp = loadXmlResourceParser(file, id, value.assetCookie, "drawable");
                dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
                rp.close();
            } else {
                //注释2处,加载 .png,.jpg 等
                final InputStream is = mAssets.openNonAsset(value.assetCookie, file, AssetManager.ACCESS_STREAMING);
                AssetInputStream ais = (AssetInputStream) is;
                dr = decodeImageDrawable(ais, wrapper, value);
            }
        } finally {
            stack.pop();
        }
    } catch(Exception | StackOverflowError e) {
        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        final NotFoundException rnf = new NotFoundException("File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
        rnf.initCause(e);
        throw rnf;
    }
    Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
    if(TRACE_FOR_DETAILED_PRELOAD) {
        if(((id >>> 24) == 0x1)) {
            final String name = getResourceName(id);
            if(name != null) {
                final long time = System.nanoTime() - startTime;
                final int loadedBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps - startBitmapCount;
                final long loadedBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize - startBitmapSize;
                final int loadedDrawables = sPreloadTracingNumLoadedDrawables - startDrawableCount;
                sPreloadTracingNumLoadedDrawables++;
                final Boolean isRoot = (android.os.Process.myUid() == 0);
                Log.d(TAG_PRELOAD, (isRoot ? "Preloaded FW drawable #" : "Loaded non-preloaded FW drawable #") + Integer.toHexString(id) + " " + name + " " + file + " " + dr.getClass().getCanonicalName() + " #nested_drawables= " + loadedDrawables + " #bitmaps= " + loadedBitmapCount + " total_bitmap_size= " + loadedBitmapSize + " in[us] " + (time / 1000));
            }
        }
    }
    return dr;
}

注释0处, 类似 res/drawable-xxhdpi-v4/avatar.png 代表是png文件,如果是xml类型的drawable,则类似 res/drawable/avatar_two.xml

注释1处,从 xml 文件加载。

注释2处,加载 .png,.jpg 等格式的文件。

总结:整体看下来大致流程还是比较简单。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值