package com.dianping.loader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.InputStream; import java.util.HashMap; import org.xmlpull.v1.XmlPullParser; import android.content.Context; import android.content.res.AssetManager; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.dianping.app.MyApplication; import com.dianping.loader.model.FileSpec; import com.dianping.loader.model.SiteSpec; public class MyResources { FileSpec file; String packageName; Resources res; AssetManager asset; MyResources[] deps; MyResources(FileSpec file, String packageName, Resources res, AssetManager asset, MyResources[] deps) { this.file = file; this.packageName = packageName; this.res = res; this.asset = asset; this.deps = deps; } /** * Resources.getDrawable(id) */ public Drawable getDrawable(int id) { return res.getDrawable(id); } /** * Resources.getText(id) */ public CharSequence getText(int id) { return res.getText(id); } /** * Resources.getString(id) */ public String getString(int id) { return res.getString(id); } /** * Resources.getStringArray(id) */ public String[] getStringArray(int id) { return res.getStringArray(id); } /** * Resources.getColor(id) */ public int getColor(int id) { return res.getColor(id); } /** * Resources.getColorStateList(id) */ public ColorStateList getColorStateList(int id) { return res.getColorStateList(id); } /** * Resources.getDimension(id) */ public float getDimension(int id) { return res.getDimension(id); } /** * Resources.getDimensionPixelSize(id) */ public int getDimensionPixelSize(int id) { return res.getDimensionPixelSize(id); } /** * Resources.getDimensionPixelOffset(id) */ public int getDimensionPixelOffset(int id) { return res.getDimensionPixelOffset(id); } /** * Resources.openRawResource(id) */ public InputStream openRawResource(int id) { return res.openRawResource(id); } public byte[] getRawResource(int id) { InputStream ins = openRawResource(id); try { int n = ins.available(); ByteArrayOutputStream bos = new ByteArrayOutputStream(n > 0 ? n : 4096); byte[] buf = new byte[4096]; int l; while ((l = ins.read(buf)) != -1) { bos.write(buf, 0, l); } ins.close(); return bos.toByteArray(); } catch (Exception e) { return new byte[0]; } } /** * 返回独立的Resources * <p> * 对Resources进行操作时不会处理依赖关系,所有依赖包的内容均不会出现在该Resources中。 * * @return */ public Resources getResources() { return res; } /** * 返回独立的AssetManager * <p> * 对AssetManager进行操作时不会处理依赖关系,所有依赖包的内容均不会出现在该AssetManager中。 * * @return */ public AssetManager getAssets() { return asset; } /** * 同LayoutInflater.inflate(id, parent, attachToRoot) * <p> * 不会处理依赖关系,请确保id对应的layout在当前包内 * * @param name * @return * @throws Resources.NotFoundException */ public View inflate(Context context, int id, ViewGroup parent, boolean attachToRoot) { if (!(context instanceof MainActivity)) { throw new RuntimeException( "unable to inflate without MainActivity context"); } MainActivity ma = (MainActivity) context; MyResources old = ma.getOverrideResources(); ma.setOverrideResources(this); try { View v = LayoutInflater.from(context).inflate(id, parent, attachToRoot); return v; } finally { ma.setOverrideResources(old); } } static final HashMap<String, MyResources> loaders = new HashMap<String, MyResources>(); /** * return null if not available on the disk */ public static MyResources getResource(SiteSpec site, FileSpec file) { MyResources rl = loaders.get(file.id()); if (rl != null) return rl; String[] deps = file.deps(); MyResources[] rs = null; if (deps != null) { rs = new MyResources[deps.length]; for (int i = 0; i < deps.length; i++) { FileSpec pf = site.getFile(deps[i]); if (pf == null) return null; MyResources r = getResource(site, pf); if (r == null) return null; rs[i] = r; } } File dir = MyApplication.instance().getFilesDir(); dir = new File(dir, "repo"); if (!dir.isDirectory()) return null; dir = new File(dir, file.id()); File path = new File(dir, TextUtils.isEmpty(file.md5()) ? "1.apk" : file.md5() + ".apk"); if (!path.isFile()) return null; try { AssetManager am = (AssetManager) AssetManager.class.newInstance(); am.getClass().getMethod("addAssetPath", String.class) .invoke(am, path.getAbsolutePath()); // parse packageName from AndroidManifest.xml String packageName = null; XmlResourceParser xml = am .openXmlResourceParser("AndroidManifest.xml"); int eventType = xml.getEventType(); xmlloop: while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_TAG: if ("manifest".equals(xml.getName())) { packageName = xml.getAttributeValue( "http://schemas.android.com/apk/res/android", "package"); break xmlloop; } } eventType = xml.nextToken(); } xml.close(); if (packageName == null) { throw new RuntimeException( "package not found in AndroidManifest.xml [" + path + "]"); } Resources superRes = MyApplication.instance().getResources(); Resources res = new Resources(am, superRes.getDisplayMetrics(), superRes.getConfiguration()); rl = new MyResources(file, packageName, res, am, rs); } catch (Exception e) { if (e instanceof RuntimeException) throw (RuntimeException) e; throw new RuntimeException(e); } loaders.put(file.id(), rl); return rl; } /** * 从当前类所在的包载入MyResource * * @param clazz * @return * @throws RuntimeException * 如果当前类不是动态加载包载入的 */ public static MyResources getResource(Class<?> clazz) { if (!(clazz.getClassLoader() instanceof MyClassLoader)) { throw new RuntimeException(clazz + " is not loaded from dynamic loader"); } return getResource((MyClassLoader) clazz.getClassLoader()); } static MyResources getResource(MyClassLoader mcl) { FileSpec file = mcl.file; MyResources rl = loaders.get(file.id()); if (rl != null) return rl; MyResources[] rs = null; if (mcl.deps != null) { rs = new MyResources[mcl.deps.length]; for (int i = 0; i < rs.length; i++) { MyResources r = getResource(mcl.deps[i]); rs[i] = r; } } File dir = MyApplication.instance().getFilesDir(); dir = new File(dir, "repo"); if (!dir.isDirectory()) throw new RuntimeException(dir + " not exists"); dir = new File(dir, file.id()); File path = new File(dir, TextUtils.isEmpty(file.md5()) ? "1.apk" : file.md5() + ".apk"); if (!path.isFile()) throw new RuntimeException(path + " not exists"); try { AssetManager am = (AssetManager) AssetManager.class.newInstance(); am.getClass().getMethod("addAssetPath", String.class) .invoke(am, path.getAbsolutePath()); Resources superRes = MyApplication.instance().getResources(); Resources res = new Resources(am, superRes.getDisplayMetrics(), superRes.getConfiguration()); // parse packageName from AndroidManifest.xml String packageName = null; XmlResourceParser xml = am .openXmlResourceParser("AndroidManifest.xml"); int eventType = xml.getEventType(); xmlloop: while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_TAG: if ("manifest".equals(xml.getName())) { packageName = xml.getAttributeValue(null, "package"); break xmlloop; } } eventType = xml.nextToken(); } xml.close(); if (packageName == null) { throw new RuntimeException( "package not found in AndroidManifest.xml [" + path + "]"); } rl = new MyResources(file, packageName, res, am, rs); } catch (Exception e) { if (e instanceof RuntimeException) throw (RuntimeException) e; throw new RuntimeException(e); } loaders.put(file.id(), rl); return rl; } }