android动态加载(二)
上一篇说了android中的动态加载,即在android工程中动态加载经过dx操作以后的jar文件和没有安装的apk文件,今天我们来看看怎么执行已经安装的apk中的类中的方法。
所以,我们会需要两个工程,一个是plugone,这个是我们暴露给外面的方法的一个android工程。另外一个我们暂且给他起名为useplugone吧。
先来看看plugone工程,我们在plugone工程中有这样一个类,用来暴露给调用者一个方法:
package com.example.plugone; public class Plugin1 { public int add(int a,int b) { return a + b; } }
另外,还需要在清单文件中为MainActivity中添加一个action,这样做是为了在useplugone工程中,找到该apk中的资源,代码如下如下:
<activity android:name="com.example.plugone.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="com.haha.android.plugin"/> </intent-filter> </activity>
//创建一个意图,用来找到指定的apk Intent intent = new Intent("com.haha.android.plugin", null); //获得包管理器 PackageManager pm = getPackageManager(); List<ResolveInfo> resolveinfoes = pm.queryIntentActivities(intent, 0); //获得指定的activity的信息 ActivityInfo actInfo = resolveinfoes.get(0).activityInfo; //获得包名 String pacageName = actInfo.packageName; //获得apk的目录或者jar的目录 String apkPath = actInfo.applicationInfo.sourceDir; //dex解压后的目录,注意,这个用宿主程序的目录,android中只允许程序读取写自己 //目录下的文件 String dexOutputDir = getApplicationInfo().dataDir; // /data/app/com.example.plugone-1.apk //native代码的目录 String libPath = actInfo.applicationInfo.nativeLibraryDir; // /data/app-lib/com.example.plugone-1 //创建类加载器,把dex加载到虚拟机中 DexClassLoader calssLoader = new DexClassLoader(apkPath, dexOutputDir, libPath, this.getClass().getClassLoader()); //利用反射调用插件包内的类的方法 try { Class<?> clazz = calssLoader.loadClass(pacageName+".Plugin1"); Object obj = clazz.newInstance(); Class[] param = new Class[2]; param[0] = Integer.TYPE; param[1] = Integer.TYPE; Method method = clazz.getMethod("add", param); Integer ret = (Integer)method.invoke(obj, 1,12); int result = ret.intValue(); System.out.println(result); } catch (Exception e) { e.printStackTrace(); }
源码下载
下面我们在重新看看在android工程中动态加载jar文件,同样该jar文件是需要经过dx处理的,不懂得可以看看上一篇文章android动态加载,在上一篇中我们导出jar文件时用的java工程,并且当时还指明导出jar文件时不要选择将接口导出,这是因为在这个android工程中同样声明了该接口,所以如果将接口导出,会出现接口冲突,这次我们改为将android工程导出为jar文件,并且可以导出接口,这次我们创建一个android工程plugtwo,然后导出jar文件,经过dx处理以后,将其放入另一个useplugtwo工程的asset文件夹下,来让程序自动运行。下面分别来看看plugtwo和useplugtwo工程的代码:
首先是plugtwo:
首先我们声明一个接口文件:
package com.example.plugtwo; public interface Iinterface { public void call(); public String getData(); }然后创建一个实体类实现该接口,这次我们让该实体类可以弹出toast,所以需要创建一个context成员变量
package com.example.plugtwo; import android.content.Context; import android.widget.Toast; public class IClass implements Iinterface { private Context context; public IClass(Context context) { super(); this.context = context; } @Override public void call() { Toast.makeText(context, "call method", 0).show(); } @Override public String getData() { return "hello,i am from IClass"; } }程序比较简单,我就不细说了。然后将该程序导出为jar文件,这里我们同样导出为load.jar,然后将该load.jar文件拷贝到具有dx命令的sdk的文件夹下,执行dx --dex --output=testdex.jar load.jar ,成功以后会发现在该文件夹下生成了一个testdex.jar文件,这个jar文件就是我们的android工程可以直接调用的jar文件。
现在我们新建一个名为useplugtwo的android工程,并且将该testdex.jar文件拷贝到useplugtwo的asset工程中,接下来看看我们的useplugtwo中的MainActivity代码:
首先我们调用copyFromAsset方法将asset文件夹下的testdex.jar文件拷贝到sdcard的指定目录,代码如下:
public void copyFromAsset() { InputStream ins = null; FileOutputStream fos = null; try { ins = getAssets().open("testdex.jar"); // String getStr = "/storage/sdcard0/liuhang/"; File file = new File(dir+"/testjar/"); if (!file.exists()) { file.mkdirs(); } file = new File(file,"testdex.jar"); fos = new FileOutputStream(file); int count = 0; byte[]b = new byte[1024]; while ((count = ins.read(b)) != -1) { fos.write(b,0,count); } } catch (Exception e) { e.printStackTrace(); }finally{ try { if (fos != null) { fos.flush(); fos.close(); } } catch (Exception e) { e.printStackTrace(); } } }
然后就是利用反射动态执行生成的testdex.jar文件中的方法,如下:
File file = new File("/storage/sdcard0/testjar/testdex.jar"); final File optimizedDexOutputPath = getDir("temp", Context.MODE_PRIVATE); String filePath = file.getAbsolutePath(); String optimizedPath = optimizedDexOutputPath.getAbsolutePath(); //optimizedPath == data/data/com.example.useplugtwo/app_temp DexClassLoader classLoader = new DexClassLoader(file.getAbsolutePath(), optimizedDexOutputPath.getAbsolutePath(), null, getClassLoader()); try { Class<?> iclass = classLoader.loadClass("com.example.plugtwo.IClass"); Constructor<?> istructor = iclass.getConstructor(Context.class); //利用反射原理去调用 Method method = iclass.getMethod("call", null); String data = (String) method.invoke(istructor.newInstance(this), null); System.out.println(data); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }
今天就到这里,该休息了。
源码连接
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。