上一篇说了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();
}
今天就到这里,该休息了。
源码连接