前言
在今天之前,我并不知道还有这么个机制。直到写的代码出了bug。在项目中使用了Tint对图片进行着色处理。
|
|
但是,所有使用icon的地方,全部被着色了。一起来探讨下。
从资源加载的角度入手
废话不多说,直接看代码。
|
|
资源的加载详细过程这里不做过多解释,在6.0的源码中 ,通过ResurcesImpl#loadDrawable来加载图片,当第一次加载我们apk中的drawable资源的时候,因为没有对应cache的关系,会从资源文件中,解析资源,并做相应的cache操作,对应代码如下:
|
|
本文以BitmapDrawable为例,因此,加载全薪资源由loadDrawableForCookie完成,而cache操作由cacheDrawable来完成,那么,我们看下cache了哪些数据。
|
|
重点看头和尾,
- dr.getConstantState
- caches.put
这里,我们不必要去关心cache的操作,我们关系的是getConstantState,以BitmapDrawable为例,他返回他的类型为BitmapState的成员。
再看setTintList与getDrawable读取cache
|
|
BitmapDrawable的setTintList方法,将mBitmapState的mTint属性做了修改,而mBitmapState是被Resource缓存起来了的,因此,缓存中对应的部分也被修改了。
在loadDrawable的方法中,有如下代码片段。
|
|
当从cache中,获取到之后,就会直接返回。再看下getInstance。
|
|
还是以BitmapDrawable为例,
|
|
因此,我们在先前修改了的mTint也被复制给了后续cache的Drawable,因此,他们也被着色了。
如何避免
想要避免这个问题,也很简单,调用Drawable#mutate方法,这个方法会返回一个新的State,我们对这个进行修改是不会作用到原先的State的.
总结
上述的Drawable.ConstantState,就是我们想说的ConstantState。这个的设计是为了节约内存。官方blog中也有对这个的介绍。详情见链接,需要翻墙。