获取未安装资源apk种的资源文件

   最近做游戏联运的sdk插件,里面涉及到了游戏的登录、应用内支付等功能,故而界面的布局是少不了的。首先,想到的是xml布局,但是,作为第三方的sdk插件,作为jar包需要加到第三方的libs中,xml界面布局是不被打包到jar包中的。虽然,xml布局文件可以通过反射获取到,但是,这对第三方的要求是所有文件必须放的位置正确,不得出现疏忽。为此,想到,如果把所有的资源文件全部放到一个apk中,然后去加载这个apk的资源,那问题岂不是都解决了?带着这个想法,一步一步来实现这个功能。

  众所周知,apk中的资源文件基本上全部放到res目录下面,所以,怎么获取到资源apk的res实体是首要目标。以下代码就是获取资源apk中的Resource:

package com.tabolt.reflect;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.content.Context;
import android.content.res.Resources;
import android.util.DisplayMetrics;

public class GetOtherApkRes {
	/**
	 *获取指定路径apk的resources
	 *@param context 上下文
	 *@param apkPath apk绝对路径 
	 */
	public static Resources getResources(Context context, String apkPath) {
		File file = new File(apkPath);
		String PATH_PackageParser = "android.content.pm.PackageParser";
		String PATH_AssetManager = "android.content.res.AssetManager";
		try {
			// 反射得到pkgParserCls对象并实例化,有参数
			Class<?> pkgParserCls = Class.forName(PATH_PackageParser);
			Class<?>[] typeArgs = { String.class };
			Constructor<?> pkgParserCt = pkgParserCls.getConstructor(typeArgs);
			Object[] valueArgs = { file.getAbsolutePath() };
			Object pkgParser = pkgParserCt.newInstance(valueArgs);

			// 从pkgParserCls类得到parsePackage方法
			DisplayMetrics metrics = new DisplayMetrics();
			metrics.setToDefaults();// 这个是与显示有关的, 这边使用默认
			typeArgs = new Class<?>[] { File.class, String.class,DisplayMetrics.class, int.class };
			Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod("parsePackage", typeArgs);
			valueArgs = new Object[] { file, file.getAbsolutePath(), metrics, 0 };

			// 执行pkgParser_parsePackageMtd方法并返回
			Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser,valueArgs);

			// 从返回的对象得到名为"applicationInfo"的字段对象
			if (pkgParserPkg == null) {
				return null;
			}
			Field appInfoFld = pkgParserPkg.getClass().getDeclaredField("applicationInfo");

			// 从对象"pkgParserPkg"得到字段"appInfoFld"的值
			if (appInfoFld.get(pkgParserPkg) == null) {
				return null;
			}
			// 反射得到assetMagCls对象并实例化,无参
			Class<?> assetMagCls = Class.forName(PATH_AssetManager);
			Object assetMag = assetMagCls.newInstance();
			// 从assetMagCls类得到addAssetPath方法
			typeArgs = new Class[1];
			typeArgs[0] = String.class;
			Method assetMag_addAssetPathMtd = assetMagCls.getDeclaredMethod("addAssetPath", typeArgs);
			valueArgs = new Object[1];
			valueArgs[0] = file.getAbsolutePath();
			// 执行assetMag_addAssetPathMtd方法
			assetMag_addAssetPathMtd.invoke(assetMag, valueArgs);
			// 得到Resources对象并实例化,有参数
			Resources res = context.getResources();
			typeArgs = new Class[3];
			typeArgs[0] = assetMag.getClass();
			typeArgs[1] = res.getDisplayMetrics().getClass();
			typeArgs[2] = res.getConfiguration().getClass();
			Constructor<Resources> resCt = Resources.class.getConstructor(typeArgs);
			valueArgs = new Object[3];
			valueArgs[0] = assetMag;
			valueArgs[1] = res.getDisplayMetrics();
			valueArgs[2] = res.getConfiguration();
			res = (Resources) resCt.newInstance(valueArgs);
			return res;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

  ok,上面的代码已经获取到了资源apk里面的Resource,然后接下来就是获取你要得到的资源了。

第一步:Resource中有个方法是getIdentifie,三个参数分别是 资源名,资源类型,资源apk包名,通过此方法可以获取到资源的id号。

第二步:上一步获取到资源id号之后,接下来就是要得到实质性的资源了。对于简单类型的资源,如:

(1)图片资源,可以获取到Drawable类型的图片

public Drawable getResApkDrawable(int id) {
	return res.getDrawable(id)
}	

(2)String类型资源,获取String文件夹中的字符串

public String getResApkString(int id) {
	return res.getString(id)
}


对于color,dimens文件中的资源,获取方式与以上两种相同,再次不在赘述。

(3.1) layout布局资源的获取,要想获取layout资源,我采用的是根据id先获取XmlPullParser,然后再调用LayoutInflater的inflate方法获取layout资源,视图view。

public View getResApkLayoutView(Context context, int id) {
	LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	return inflater.inflate(res.getLayout(id), null);
}

(3.2) 上一步获取到layout布局,然后就是要获取到layout中控件的实体view了,很简单,调用findViewById就ok了

public View getResApkWidgetView(View layout, int id) {
	return layout.findViewById(id);
}

(4) anim动画资源的获取,res中没有方法可以直接获取到Animation,只有方法getAnimation获取到XmlPullParser,查询AnimationUtils的loadAnimation方法,发现加载Animation的最后一步是方法createAnimationFromXml,此方法的源码是:

private Animation createAnimationFromXml(Context c, XmlPullParser parser,AnimationSet parent,AttributeSet attrs)									throws XmlPullParserException, IOException {

	Animation anim = null;
	int type;
	int depth = parser.getDepth();
	while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && 
		type != XmlPullParser.END_DOCUMENT) {

			if (type != XmlPullParser.START_TAG) {
				continue;
			}
			String name = parser.getName();
			if (name.equals("set")) {
				anim = new AnimationSet(c, attrs);
				createAnimationFromXml(c, parser, (AnimationSet) anim, attrs);
			} else if (name.equals("alpha")) {
				anim = new AlphaAnimation(c, attrs);
			} else if (name.equals("scale")) {
				anim = new ScaleAnimation(c, attrs);
			} else if (name.equals("rotate")) {
				anim = new RotateAnimation(c, attrs);
			} else if (name.equals("translate")) {
				anim = new TranslateAnimation(c, attrs);
			} else {
				throw new RuntimeException("Unknown animation name: "+ parser.getName());
			}
			if (parent != null) {
				parent.addAnimation(anim);
			}
		}
		return anim;
	}

还有就是attrs的获取,AttributeSet attrs = Xml.asAttributeSet(parser);至此,动画Animation的获取算是完毕,最后的调用方法是:

public Animation getResApkAnim(Context context, int id) {
	Animation animation = null;
	XmlPullParser parser = res.getAnimation(id);	
	AttributeSet attrs = Xml.asAttributeSet(parser);
	try {
		animation = createAnimationFromXml(context, parser, null, attrs);
	} catch (XmlPullParserException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	return animation;
}

好了,到现在为止,资源apk中的要获取的资源现在都可以获取的到了,需要注意的是,获取layout布局视图view的时候,布局中用到的@资源的设置失效,不建议使用。

                                                                            demo文件下载地址请点击这里!!



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值