Android 动态加载jar/APK
2015-04-28 16:25
337 查看
可以使用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才行,执行命令:
(注意:打包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)
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才行,执行命令:
$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)
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包
public void loadDexFile(Context context) { copyAssetsToFiles(context); String filePath = context.getFilesDir().getPath() + "/testDex.jar"; //动态加载的也可以是APK文件,没有任何区别,APK 文件中的class.dex文件会被DexClassLoader加载。 //但是,APK中的Activity类,由于是使用反射,无法取得Context,与普通的类毫无区别,没有生命周期。 //String filePath = context.getFilesDir().getPath() + "/Test.apk"; 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); //method.setAccessible(true);访问private函数 System.out.println(data); //方法二:类型强转,前提是知道Test.jar中Class所实现的接口类<span style="font-family: Arial, Helvetica, sans-serif;">ITest</span> 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(); } } /** * Copy the APK "assets/" to "/data/data/package-name/file/" */ 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); } } /** * Copy assets file to the data folder */ 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示例代码:
/** * 在APK1中调用手机上已经安装的APK2中的类 */ public void loadAPKFile(){ //在APK1中已知APK2的包名,寻找APK2文件所在路径 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; //目标类所在的apk路径,class loader会通过这个路径来加载目标类文件 String dexPath = activityInfo.applicationInfo.sourceDir; //Classloader用来释放dex文件的输出路径 String dexOutputDir = mContext.getApplicationInfo().dataDir; //目标类可能使用的c或者c++的库文件的存放路径 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"); } }
相关文章推荐
- Android动态加载jar、apk的实现
- Android DexClassLoader/PathClassLoader 动态加载jar/APK
- Android动态加载jar,dex,apk文件
- Android动态加载jar、apk的实现
- Android使用DexClassLoader 动态加载jar/apk
- Android动态加载jar/dex/apk
- Android使用DexClassLoader 动态加载jar/apk
- Android动态加载jar、apk的实现
- Android动态加载jar、apk的实现
- android 动态加载jar/dex/apk
- Android动态加载jar、apk的实现
- 在Android 4.4/5.0/8.0 测试动态加载jar/apk
- Android动态加载jar、apk的实现
- Android动态加载jar、apk的实现
- Android动态加载jar、apk的实现
- 系统入门(2):Android动态加载jar、apk的实现
- Android动态加载jar、apk的实现
- Android动态加载jar、apk的实现
- Android动态加载jar、apk的实现
- Android动态加载jar、apk的实现