我正在构建一个家庭自动化应用程序。 我正在尝试添加一个插件系统。 作为测试,我将测试类(该子类按钮)导出为APK文件,并将其放在我的应用程序的文件目录中。 我能够创建此类的新实例,并使用 DexClassLoader
和 .loadClass
将其放入我的视图中。
下一步是扫描此目录中的所有APK并获取其中类的名称。
我找到了 DexFile 类,它就是这样做的,但它会引发以下异常:
04-18 17:26:15.697: E/dalvikvm(726): Can't open dex cache '/data/dalvik-cache/data@data@com.strutton.android.testplugin@files@testloadclass.apk@classes.dex': No such file or directory
04-18 17:26:15.705: I/dalvikvm(726): Unable to open or create cache for /data/data/com.strutton.android.testplugin/files/testloadclass.apk (/data/dalvik-cache/data@data@com.strutton.android.testplugin@files@testloadclass.apk@classes.dex)
似乎它正在尝试在系统缓存中找到优化的 DexFile,但我没有缓存目录的权限。 据我所知,这可能是设计使然,我对此没有问题。 有没有另一种方法可以解析 DEX 文件并从中获取类名?
我已经查看了一些 dex 反编译器项目的源代码。 我必须推出自己的解决方案吗?
这是我的测试应用程序代码(来自我的活动OnCreate),以防万一我错过了什么:
try {
ArrayList<String> UIPlugins = new ArrayList<String>();
final File filesDir = this.getFilesDir();
final File tmpDir = getDir("dex", 0);
final DexClassLoader classloader = new DexClassLoader( filesDir.getAbsolutePath()+"/testloadclass.apk",
tmpDir.getAbsolutePath(),
null, this.getClass().getClassLoader());
final Class<Button> classToLoad =
(Class<Button>) classloader.loadClass("com.strutton.android.testloadclass.MyTestClass_IRDroidUIPlugIn");
Button mybutton = classToLoad.getDeclaredConstructor(Context.class).newInstance(this);
mybutton.setId(2);
mybutton.setOnClickListener(this);
main.addView(mybutton);
// Up to here everything works as expected
// This line throws the exceptions
DexFile mDexFile = new DexFile(filesDir.getAbsolutePath()+"/testloadclass.apk";);
Enumeration<String> classNames = mDexFile.entries();
while (classNames.hasMoreElements()){
String className = classNames.nextElement();
if (className.endsWith("IRDroidUIPlugIn")){
UIPlugins.add(className);
}
}
final Class<Button> classToLoad =
(Class<Button>) classloader.loadClass("com.strutton.android.testloadclass.MyTestClass_IRDroidUIPlugIn");
Button mybutton = classToLoad.getDeclaredConstructor(Context.class).newInstance(this);
mybutton.setId(2);
mybutton.setOnClickListener(this);
main.addView(mybutton);
btn.setText(tmpDir.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
而不是:
DexFile mDexFile = new DexFile(filesDir.getAbsolutePath()+"/testloadclass.apk";);
尝试:
DexFile mDexFile = DexFile.loadDex(filesDir.getAbsolutePath()+"/testloadclass.apk", tmpDir.getAbsolutePath()+"/testloadclass.odex", 0);
考虑的另一种方法是将插件安装为单独的应用程序。您可以让插件应用程序使用您定义的特定意图公开服务。您还需要一个服务将实现的预定义 aidl 接口。然后,在主应用程序中,您可以解析该服务意图,以查找任何插件服务。