可以使用Android ClassLoader完成DEX的动态加载,DEX文件可以附属在assets或raw目录也可以运行时从网络下载。
1.当前动态加载常用的class loader
(1)DexClassLoader:这个可以加载jar/apk/dex,也可以从SD卡中加载,也是本文的重点。
(2)PathClassLoader:只能加载dex文件和已经安装到Android系统中的apk文件。
这两者的区别在于DexClassLoader需要提供一个可写的outpath路径,用来释放.apk包或者.jar包中的dex文件。换个说法来说,就是PathClassLoader不能主动从zip包中释放出dex,因此只支持直接操作dex格式文件,或者已经安装的apk(因为已经安装的apk在cache中存在缓存的dex文件)。而DexClassLoader可以支持.apk、.jar和.dex文件,并且会在指定的outpath路径释放出dex文件。另外,PathClassLoader在加载类时调用的是DexFile的loadClassBinaryName,而DexClassLoader调用的是loadClass。因此,在使用PathClassLoader时类全名需要用”/”替换”.”。
2.首先将打包好的 jar 转为dex 格式。在Android中无法像Java中那样方便动态加载jar, Android的虚拟机(Dalvik VM)是不认识Java打出jar的byte code,需要通过dx工具来优化转换成Dalvik byte code才行,执行命令:
[cpp] view plain copy
- $dx --dex --output=testDex.jar test.jar
(注意:打包test.jar时请不要把接口文件打进来. 否则加载时会报错:java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation)
3. 动态加载jar 包示例代码:
首先编写接口和实现,生成jar包(打包jar的时候不要把接口ITest.class打包进来,否则加载时会有冲突报错。之所以定义接口ITest是为了说明下面动态加载方法二:类型强转,前提是知道Test.jar中Class所实现的接口类ITest)
[java] view plain copy
- package com.test.mytest;
-
- public interface ITest {
- public String test();
- }
- package com.test.mytest;
-
- public class Test implements ITest {
- @Override
- public String test() {
- return "Hello";
- }
- }
动态加载jar包
[java] view plain copy
- public void loadDexFile(Context context) {
- copyAssetsToFiles(context);
- String filePath = context.getFilesDir().getPath() + "/testDex.jar";
-
-
-
-
-
- DexClassLoader classLoader = new DexClassLoader(filePath, context.getFilesDir().getPath(), null, context.getClassLoader());
- try {
-
- Class myClass = classLoader.loadClass("com.test.mytest.Test");
- Constructor myConstructor = myClass.getConstructor(Context.class);
- Method method = myClass.getMethod("test", null);
- String data = (String) method.invoke(myConstructor.newInstance(this), null);
-
- System.out.println(data);
-
-
- Class myClass2 = classLoader.loadClass("com.test.mytest.Test");
- Constructor myConstructor2 = myClass2.getConstructor(Context.class);
- ITest obj = (ITest) myConstructor2.newInstance(this);
- String data2 = obj.test();
- System.out.println(data2);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
-
-
-
- public static void copyAssetsToFiles(Context context) {
- String fileDir = context.getFilesDir().getPath() + "/";
- File workingDir = new File(fileDir);
- if (!workingDir.exists()) {
- workingDir.mkdirs();
- }
-
- File outFile_bin = new File(workingDir, "test.jar");
- if (!outFile_bin.exists()) {
- copyFile(context, "test.jar", outFile_bin);
- outFile_bin.setExecutable(true, false);
- }
- }
-
-
-
-
- private static void copyFile(Context context, String sourceFileName, File targetFile) {
- InputStream in = null;
- FileOutputStream out = null;
- try {
- in = context.getAssets().open(sourceFileName);
- out = new FileOutputStream(targetFile);
- byte[] temp = new byte[1024];
- int count = 0;
- while ((count = in.read(temp)) > 0) {
- out.write(temp, 0, count);
- }
-
- if (in != null) {
- in.close();
- }
- if (out != null) {
- out.close();
- }
- } catch (Exception e) {
- if (in != null) {
- try {
- in.close();
- } catch (IOException e1) {
- e1.printStackTrace();
- }
- }
-
- if (out != null) {
- try {
- out.close();
- } catch (IOException e1) {
- e1.printStackTrace();
- }
- }
- }
- }
4. 动态加载APK示例代码:
[java] view plain copy
-
-
-
- public void loadAPKFile(){
-
- Intent intent = new Intent();
- intent.setPackage("com.example.test");
- PackageManager pm = mContext.getPackageManager();
- final List<ResolveInfo> plugins = pm.queryIntentActivities(intent,0);
- if(plugins.size() <= 0){
- Log.i(TAG, "resolve info size is:" + plugins.size());
- return;
- }
-
- ResolveInfo resolveInfo = plugins.get(0);
- ActivityInfo activityInfo = resolveInfo.activityInfo;
- String packageName = activityInfo.packageName;
-
- String dexPath = activityInfo.applicationInfo.sourceDir;
-
- String dexOutputDir = mContext.getApplicationInfo().dataDir;
-
- String libPath = activityInfo.applicationInfo.nativeLibraryDir;
-
- Log.i(TAG, "div:" + div + " " +
- "packageName:" + packageName + " " +
- "dexPath:" + dexPath + " " +
- "dexOutputDir:" + dexOutputDir + " " +
- "libPath:" + libPath);
-
- DexClassLoader dcLoader = new DexClassLoader(dexPath, dexOutputDir,libPath,this.getClass().getClassLoader());
- try {
- Class<?> clazz = dcLoader.loadClass(packageName + ".TestClass");
- Object obj = clazz.newInstance();
- Class[] param = new Class[1];
- param[0] = String.class;
- Method action = clazz.getMethod("invoke", param);
- action.invoke(obj, "test this function");
- } catch (ClassNotFoundException e) {
- Log.i(TAG, "ClassNotFoundException");
- } catch (InstantiationException e) {
- Log.i(TAG, "InstantiationException");
- } catch (IllegalAccessException e) {
- Log.i(TAG, "IllegalAccessException");
- } catch (NoSuchMethodException e) {
- Log.i(TAG, "NoSuchMethodException");
- } catch (IllegalArgumentException e) {
- Log.i(TAG, "IllegalArgumentException");
- } catch (InvocationTargetException e) {
- Log.i(TAG, "InvocationTargetException");
- }
- }