package com.v7lin.android.env; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import android.annotation.TargetApi; import android.content.res.AssetManager; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable.ConstantState; import android.os.Build; import android.util.DisplayMetrics; import android.util.LongSparseArray; import android.util.TypedValue; /** * 仅供测试使用 * * 读取插件包资源 * * @author v7lin Email:v7lin@qq.com */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public class EnvThirdResources extends Resources { private final LongSparseArray<WeakReference<Drawable.ConstantState>> mDrawableCache = new LongSparseArray<WeakReference<Drawable.ConstantState>>(0); private final LongSparseArray<WeakReference<ColorStateList>> mColorStateListCache = new LongSparseArray<WeakReference<ColorStateList>>(0); private final LongSparseArray<WeakReference<Drawable.ConstantState>> mColorDrawableCache = new LongSparseArray<WeakReference<Drawable.ConstantState>>(0); private final Object mAccessLock = new Object(); private TypedValue mTmpValue = new TypedValue(); public EnvThirdResources(AssetManager assets, DisplayMetrics metrics, Configuration config) { super(assets, metrics, config); } @Override public int getColor(int id) throws NotFoundException { TypedValue value; synchronized (mAccessLock) { value = mTmpValue; if (value == null) { value = new TypedValue(); } getValue(id, value, true); if (value.type >= TypedValue.TYPE_FIRST_INT && value.type <= TypedValue.TYPE_LAST_INT) { mTmpValue = value; 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"); } mTmpValue = null; } ColorStateList csl = loadColorStateList(value, id); synchronized (mAccessLock) { if (mTmpValue == null) { mTmpValue = value; } } return csl.getDefaultColor(); // return super.getColor(id); } @Override public ColorStateList getColorStateList(int id) throws NotFoundException { TypedValue value; synchronized (mAccessLock) { value = mTmpValue; if (value == null) { value = new TypedValue(); } else { mTmpValue = null; } getValue(id, value, true); } ColorStateList res = loadColorStateList(value, id); synchronized (mAccessLock) { if (mTmpValue == null) { mTmpValue = value; } } return res; // return super.getColorStateList(id); } private ColorStateList loadColorStateList(TypedValue value, int id) throws NotFoundException { final long key = (((long) value.assetCookie) << 32) | value.data; ColorStateList csl; if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { csl = ColorStateList.valueOf(value.data); return csl; } csl = getCachedColorStateList(key); if (csl != null) { return csl; } if (value.string == null) { throw new NotFoundException("Resource is not a ColorStateList (color or path): " + value); } String file = value.string.toString(); if (file.endsWith(".xml")) { try { XmlResourceParser rp = loadXmlResourceParserReflect(file, id, value.assetCookie, "colorstatelist"); if (rp == null) { rp = loadXmlResourceParserEqual(file, id, value.assetCookie, "colorstatelist"); } csl = ColorStateList.createFromXml(this, rp); rp.close(); } catch (Exception e) { NotFoundException rnf = new NotFoundException("File " + file + " from color state list resource ID #0x" + Integer.toHexString(id)); rnf.initCause(e); throw rnf; } } else { throw new NotFoundException("File " + file + " from drawable resource ID #0x" + Integer.toHexString(id) + ": .xml extension required"); } if (csl != null) { synchronized (mAccessLock) { mColorStateListCache.put(key, new WeakReference<ColorStateList>(csl)); } } return csl; } private ColorStateList getCachedColorStateList(long key) { synchronized (mAccessLock) { WeakReference<ColorStateList> wr = mColorStateListCache.get(key); if (wr != null) { ColorStateList entry = wr.get(); if (entry != null) { return entry; } else { mColorStateListCache.delete(key); } } } return null; } @Override public Drawable getDrawable(int id) throws NotFoundException { TypedValue value; synchronized (mAccessLock) { value = mTmpValue; if (value == null) { value = new TypedValue(); } else { mTmpValue = null; } getValue(id, value, true); } Drawable res = loadDrawable(value, id); synchronized (mAccessLock) { if (mTmpValue == null) { mTmpValue = value; } } return res; // return super.getDrawable(id); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) @Override public Drawable getDrawableForDensity(int id, int density) throws NotFoundException { TypedValue value; synchronized (mAccessLock) { value = mTmpValue; if (value == null) { value = new TypedValue(); } else { mTmpValue = null; } getValueForDensity(id, density, value, true); DisplayMetrics metrics = getDisplayMetrics(); if (value.density > 0 && value.density != TypedValue.DENSITY_NONE) { if (value.density == density) { value.density = metrics.densityDpi; } else { value.density = (value.density * metrics.densityDpi) / density; } } } Drawable res = loadDrawable(value, id); synchronized (mAccessLock) { if (mTmpValue == null) { mTmpValue = value; } } return res; // return super.getDrawableForDensity(id, density); } private Drawable loadDrawable(TypedValue value, int id) throws NotFoundException { boolean isColorDrawable = false; if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { isColorDrawable = true; } final long key = isColorDrawable ? value.data : (((long) value.assetCookie) << 32) | value.data; Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key); if (dr != null) { return dr; } if (isColorDrawable) { dr = new ColorDrawable(value.data); } if (dr == null) { if (value.string == null) { throw new NotFoundException("Resource is not a Drawable (color or path): " + value); } String file = value.string.toString(); if (file.endsWith(".xml")) { try { XmlResourceParser rp = loadXmlResourceParserReflect(file, id, value.assetCookie, "drawable"); if (rp == null) { rp = loadXmlResourceParserEqual(file, id, value.assetCookie, "drawable"); } dr = Drawable.createFromXml(this, rp); rp.close(); } catch (Exception e) { NotFoundException rnf = new NotFoundException("File " + file + " from drawable resource ID #0x" + Integer.toHexString(id)); rnf.initCause(e); throw rnf; } } else { try { InputStream is = openNonAssetReflect(value.assetCookie, file, AssetManager.ACCESS_STREAMING); if (is == null) { is = openNonAssetEqual(id); } dr = Drawable.createFromResourceStream(this, value, is, file, null); is.close(); } catch (Exception e) { NotFoundException rnf = new NotFoundException("File " + file + " from drawable resource ID #0x" + Integer.toHexString(id)); rnf.initCause(e); throw rnf; } } } if (dr != null) { dr.setChangingConfigurations(value.changingConfigurations); Drawable.ConstantState cs = dr.getConstantState(); if (cs != null) { synchronized (mAccessLock) { if (isColorDrawable) { mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); } else { mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); } } } } return dr; } private Drawable getCachedDrawable(LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) { synchronized (mAccessLock) { WeakReference<Drawable.ConstantState> wr = drawableCache.get(key); if (wr != null) { Drawable.ConstantState entry = wr.get(); if (entry != null) { return entry.newDrawable(this); } else { drawableCache.delete(key); } } } return null; } /** * 反射方法 */ private XmlResourceParser loadXmlResourceParserReflect(String file, int id, int assetCookie, String type) { try { Method method = Resources.class.getMethod("loadXmlResourceParser", new Class[] { String.class, int.class, int.class, String.class }); method.setAccessible(true); return (XmlResourceParser) method.invoke(this, file, id, assetCookie, type); } catch (NoSuchMethodException e) { // ignore } catch (IllegalAccessException e) { // ignore } catch (IllegalArgumentException e) { // ignore } catch (InvocationTargetException e) { // ignore } return null; } /** * 等效方法 */ private XmlResourceParser loadXmlResourceParserEqual(String file, int id, int assetCookie, String type) { try { XmlResourceParser parser = getAssets().openXmlResourceParser(assetCookie, file); return parser; } catch (IOException e) { e.printStackTrace(); } return null; } /** * 反射方法 */ private InputStream openNonAssetReflect(int cookie, String fileName, int accessMode) throws IOException { try { Method method = AssetManager.class.getMethod("openNonAsset", new Class[] { int.class, String.class, int.class }); method.setAccessible(true); return (InputStream) method.invoke(getAssets(), cookie, fileName, accessMode); } catch (NoSuchMethodException e) { // ignore } catch (IllegalAccessException e) { // ignore } catch (IllegalArgumentException e) { // ignore } catch (InvocationTargetException e) { // ignore } return null; } /** * 等效方法 */ private InputStream openNonAssetEqual(int id) { return openRawResource(id); } }