实现动态加载dex
最近在做的项目是把一个dex的jar文件放置在assets下,程序启动时,从assets目录拷贝一份文件到sd以及app的data\data区,并在程序初次运行时检测是否是服务器上的最新版本,若不是则从服务器下载最新版本替换掉sd卡以及data\data去的jar,就可实现随时自更新的得动态加载,本文在这里讲述如何加载存放在sd卡的插件;
第一步首先生成一个用于启动的接口jar
这么做是为引用用以及项目打包方便,这里例如生成PlugPlatform.jar,关于如何生成只含此接口的jar的方法如下(这里说的是Android Studio的方法,Eclipse可直接选择单个文件)

在build.gradle文件中自定义makeJar ,点击Sync Now,然后在左侧的gradle的任务列表中找到并点击运行即可生成 
 task makeJar(type: Jar, dependsOn: “build”) { 
 archiveName = ‘PlugPlatform’ + “.jar”//生成需要的jar名称 
 from(‘build/intermediates/classes/release/’)//所有class文件的位置 
 from fileTree(dir: ‘src/’, includes: [‘com/adcube/resources/’])//图片资源的位置 
 exclude(‘com/manny/tent/’)//不需要的类 
 exclude(‘com/manny/utils/’)//不需要的类 
 exclude(‘*R.class’) 
 exclude(‘R$.class’) 
 destinationDir = file(‘build’) //生成的jar路径 
 第二步生成dex文件 
 在插件apk中引用PlugPlatform.jar但最后不参与编译,在插件的启动类中实现PlugPlatform这个接口并实现init和deinit方法,对应的效果分别是启动和退出,项目中的相关功能可在init中实现,项目需要生成dex文件,利用Android Studio 快速打包apk Build—>Build Apk(s)生成apk格式的文件其中就包含需要的的dex文件(在打包时在build.gradle文件中依赖 compileOnly files(‘libs/PlugPlatform.jar’)); 
 第三步在宿主应用中实现动态加载插件 
 在宿主项目中依赖PlugPlatform.jar 
 implementation files(‘libs/PlugPlatform.jar’) 
 首先我们需要创建一个ClassLoader,ClassLoader是抽象类,一般使用DexClassLoader或者PathClassLoader加载,他们的区别是 
 * DexClassLoader可以加载jar/apk/dex,可以从SD卡中加载未安装的apk 
 * PathClassLoader只能加载系统中已经安装过的apk 
 这里我们需要加载SD位置的jar,创建ClassLoader 如下: 
 File optimizedDirectory = getDir( “appdex”, MODE_PRIVATE );//解压出的dex文件的存放路径 
 String dexPath = Environment.getExternalStorageDirectory().getAbsolutePath() + “/PlugPlatform.jar”;//指目标类所在的jar/apk文件路径 
 BaseDexClassLoader baseDexClassLoader = new DexClassLoader( dexPath, optimizedDirectory.getAbsolutePath(), null, MainActivity.class.getClassLoader() ); 
 下面是封装的获取classLoader的方法 
 ClassLoader classLoader = createClassLoader( this, baseDexClassLoader ); 
 public static ClassLoader createClassLoader( 
 Context mContext, 
 BaseDexClassLoader paramBaseDexClassLoader ) 
 { 
 ClassLoader localClassLoader = mContext.getClassLoader(); 
 try 
 { 
 Field localField1 = BaseDexClassLoader.class.getDeclaredField( “pathList” ); 
 localField1.setAccessible( true ); 
 Object localObject1 = localField1.get( paramBaseDexClassLoader ); 
 Object localObject2 = localField1.get( localClassLoader ); 
 Field localField2 = localObject1.getClass().getDeclaredField( “dexElements” ); 
 localField2.setAccessible( true ); 
 Object[] arrayOfObject1 = ( Object[] )localField2.get( localObject2 ); 
 Object[] arrayOfObject2 = ( Object[] )localField2.get( localObject1 ); 
 Object[] arrayOfObject3 = ( Object[] )Array.newInstance( arrayOfObject1.getClass().getComponentType(), arrayOfObject1.length + arrayOfObject2.length ); 
 System.arraycopy( arrayOfObject2, 0, arrayOfObject3, 0, arrayOfObject2.length ); 
 System.arraycopy( arrayOfObject1, 0, arrayOfObject3, arrayOfObject2.length, arrayOfObject1.length ); 
 localField2.set( localObject2, arrayOfObject3 ); 
 return localClassLoader; 
 } 
 catch( Exception localException ) 
 { 
 return null; 
 } 
 }然后就可以利用反射来获取插件中的启动类的实例 
 PlugPlatform plugPlatform= ( PlugPlatform )classLoader.loadClass( “com.plug.test.AppPlug” ).newInstance(); 
 然后通过plugPlatform.init( this )就启动了插件功能