Android动态加载包含so文件的jar的自定义view控件

7 篇文章 0 订阅
2 篇文章 0 订阅

公司要求把某自定义view控件打包成jar,提供给某项目(这里叫它APP吧)通过网络下载的方式实现动态加载控件,该APP通过反射来构造出该view,并且调用里面的方法。这样通过反射动态加载的方式(暂且叫他反射方式)和普通的把自定义view的jar导入工程预先加载的方式(暂且叫它预先加载)不同的是


1.预先加载是先把jar复制到工程下的lib目录,然后 build path,使用该自定义的方法是通过 new 这个view的构造方法来产生,然后再通过该view的实例对象来操作里面的public方法。
2.反射动态加载的方式就比较酷炫了,可以通过网络下载jar和它的一些资源文件(比如so文件、drawable资源、assets资源等等),实现动态添加APP的功能模块,首先通过dexClassLoader来加载下载下来的jar(比如保存在SD卡某目录),然后通过反射调用,从而实例出该view使用动态加载自定义view的好处

(1)当更新APP某功能模块时,不需要用户再次下载整个APP来重新安装,只需要下载新jar来重新 动态加载就OK了(要知道,让用户下完整个APP来更新的用户体检很差的,动不动就是下载安 装)。

(2)减小APP的体积,用户需要哪些功能模块,就让他去自由的选择下载(对应jar和所需的资源文 件),反正我是不想看到APP里我不需要的功能。


开始现实动态加载
1.编写完自定义view的代码时,如果有drawable 文件,不能用普通R.来获取,不然会报找不到资源id错误,应该这么获取drawable的id:
</pre><pre name="code" class="java">public class ResourceUtils {
	public static int getIdByName(Context context, String className, String name) {
		String packageName = context.getPackageName();
		Class r = null;
		int id = 0;
		try {
			r = Class.forName(packageName + ".R");

			Class[] classes = r.getClasses();
			Class desireClass = null;

			for (int i = 0; i < classes.length; ++i) {
				if (classes[i].getName().split("\\$")[1].equals(className)) {
					desireClass = classes[i];
					break;
				}
			}

			if (desireClass != null)
				id = desireClass.getField(name).getInt(desireClass);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}

		return id;
	}
path传入SD卡上的drawable资源(不能再把图片等资源放在drawable目录下了)

2.如果有用到JNI的,不能把so文件一起打包到jar里面,需要在程序加载view前拷贝到APP的data/data/包名下的某目录(可以自己新建),然后把所有System.loadLibrary()改成 System.load(),路径就是刚才存放so文件的路径。拷贝代码如下:
public static void copySoLib(Context context, String dexPath, String nativeLibDir) {
        try {
            ZipFile zip = new ZipFile(dexPath);
            Enumeration<? extends ZipEntry> entries = zip.entries();
            while (entries.hasMoreElements()) {
                ZipEntry ze = (ZipEntry) entries.nextElement();
                if (ze.isDirectory()) {
                    continue;
                }
                String zipEntryName = ze.getName();
                if (zipEntryName.endsWith(".so")) {
                    String libName = zipEntryName.substring(zipEntryName.lastIndexOf("/") + 1);
                    InputStream ins = zip.getInputStream(ze);
                    FileOutputStream fos = new FileOutputStream(new File(nativeLibDir, libName));
                    byte[] buf = new byte[8192];
                    int len = -1;

                    while ((len = ins.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                    }
                    fos.flush();
                    fos.close();
                    ins.close();
                    Log.d(TAG, "copy so lib success: " + zipEntryName);
                }
            }
            zip.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
dexPath是so的压缩包在SD卡的路径。

3.在APP用dexClassLoader来加载jar,拿到class后再用它获取构造方法,再用该构造方法newInstance得到view的最终实例对象。代码如下:
ClassLoader dexLoader = new DexClassLoader(jarPath, dexPath, libPath, context.getClassLoader());
Class<?> mClass = dexLoader.loadClass("com.xxx.xxx.MyPluginView");
Object mViewObj = jarConstructor.newInstance(context);
mViewObj就是我们实例出来的view了,可以强制类型转换成view然后直接拿来add。

4.反射调用方法。
Method method = mClass.getMethod("方法名",int.class,........);
method.invoke(mViewObj,  new Object[]{width});

到这里就可以实现和jar里面的view交互了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值