我有一个Android应用程序,它必须加载动态类,一个未定义数量的实现接口的jar类。
实际上,我查看一个目录并列出该目录中的所有jar文件 我打开jar文件的清单,找到关联的类并列出它们。 之后,我安装了一个dexClassLoader来加载所有jar文件,并查找我在manisfest中找到的类是否实现了我的界面。 像这样我可以拥有实现我的界面的所有类,而不知道它们在前面的
要恢复,我有一个实现我的界面的类jar列表,但我的android应用程序和我都知道该列表。每次启动我的应用程序时,jar类的列表都会更改。
但是当我尝试创建DexClassLoader时,它失败了。我总是一个空指针
DexClassLoader classLoader = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),dexOutputDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());
为了进行测试,我使用了模拟器。我已将我的DDMS jar文件复制到目录中 /data/data/com.example.Myappli/JarFilesDirectory / *。罐
请注意我的jar文件内容是dex文件
我读了很多关于此事的内容。一些权限问题 我已经尝试了所有的东西,但没有找到解决方案
请有人帮帮我!!!
这里是jar文件清单的内容
清单 - 版本:1.0
Module-Class:com.example.asktester.AskPeripheral
这是我的代码:
public class ModuleLoader {
private static List<URL> urls = new ArrayList<URL>();
private static List<String> getModuleClasses(String folder)
{
List<String> classes = new ArrayList<String>();
//we are listing the jar files
File[] files = new File(folder).listFiles(new ModuleFilter());
for(File f : files)
{
JarFile jarFile = null;
try
{
//we open the jar file
jarFile = new JarFile(f);
//we recover the manifest
Manifest manifest = jarFile.getManifest();
//we recover the class
String moduleClassName = manifest.getMainAttributes().getValue("Module-Class");
classes.add(moduleClassName);
urls.add(f.toURI().toURL());
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if(jarFile != null)
{
try
{
jarFile.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
return classes;
}
private static class ModuleFilter implements FileFilter {
@Override
public boolean accept(File file) {
return file.isFile() && file.getName().toLowerCase().endsWith(".jar");
}
}
private static ClassLoader classLoader;
public static List<IPeripheral> loadModules(String folder, Context CurrentContext) throws IOException, ClassNotFoundException
{
List<IPeripheral> modules = new ArrayList<IPeripheral>();
List<String> classes = getModuleClasses(folder);
final File dexInternalStoragePath = new File(CurrentContext.getDir("dex", Context.MODE_PRIVATE),"ask.dex");
File dexOutputDir = CurrentContext.getDir("dex", Context.MODE_PRIVATE);
final File dexClasses = new File(CurrentContext.getDir("dex", Context.MODE_PRIVATE),"ASK.jar");
DexFile dexFile = DexFile.loadDex(dexClasses.getAbsolutePath(), dexOutputDir.getAbsolutePath(), 0);
DexClassLoader classLoader = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),dexOutputDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());
//Class<?> myClass = classLoader.loadClass("com.example.asktester.AskPeripheral");
if(IPeripheral.class.isAssignableFrom(myClass )){
Class<IPeripheral> castedClass = (Class<IPeripheral>)myClass ;
IPeripheral module = castedClass.newInstance();
modules.add(module);
}
}
catch (ClassNotFoundException e1)
{
e1.printStackTrace();
}
catch (InstantiationException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}
return modules;
}
答案 0 :(得分:16)
我找到了解决问题的方法。
要动态加载jar,在android应用程序中实现接口的类,需要在jar中完成一些工作:
为jar创建自己的manisfest并提供此信息
Manifest-Version: 1.0
Module-Class: com.example.myjar.MyPeripheral
使用eclipse导出你的jar并输入它使用自己的manisfest的参数
创建与jar相关联的classes.dex (这个文件是Dalvik VM所需要的,dalvik VM无法读取jar文件)
dx --dex --output=C:\classes.dex C:\MyJar.jar
小心,dex文件的名称必须 classes.dex
在jar文件中添加文件classes.dex
aapt add C:\MyJar.jar C:\classes.dex
您还需要有权写入dalvik缓存目录
adb shell chmod 777 /data/dalvik-cache
每次都重新启动模拟器
将此jar文件放入模拟器中,例如SDcard
使用PathClassLoader加载jar文件
dalvik.system.PathClassLoader myClassLoader = new dalvik.system.PathClassLoader("/Sdcard/MyJar.jar", ModuleLoader.class.getClassLoader());
注意:Eclipse中的LogCat为您提供了宝贵的信息。不要忘记查看其消息
下面是代码:
我的界面:
package com.example.StandartPeripheral;
public interface IPeripheral {
public boolean Initialize();
public boolean configure();
public boolean execute();
public String GetName();
}
实现界面的MyPeripheral
public class MyPeripheral implements IPeripheral {
//public static void main(String[] args) {}
private final String PeripheralName = "MyPeripheral";
public boolean Initialize()
{
System.out.println("Initialize ");
return true;
};
public boolean configure()
{
System.out.println("Configure !");
return true;
};
public boolean execute()
{
System.out.println("Execute !");
return true;
};
public String GetName()
{
return PeripheralName;
}
}
如何动态加载jar文件
package com.example.ModuleLoader;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import com.example.StandartPeripheral.IPeripheral;
public class ModuleLoader {
private static List<URL> urls = new ArrayList<URL>();
// to retrieve the unknown list of jar files contained in the directory folder
// in my case it was in the SDCard folder
// link to create a SDCard directory on the Eclipse emulator
// http://blog.lecacheur.com/2010/01/14/android-avoir-acces-a-une-carte-memoire-dans-lemulateur/
// retrieve the classes of all this jar files and their URL (location)
private static List<String> getModuleClasses(String folder)
{
List<String> classes = new ArrayList<String>();
//we are listing the jar files
File[] files = new File(folder).listFiles(new ModuleFilter());
for(File f : files)
{
JarFile jarFile = null;
try
{
//we open the jar file
jarFile = new JarFile(f);
//we recover the manifest
Manifest manifest = jarFile.getManifest();
//we recover the class name of our peripherals thanks to ours manifest
String moduleClassName = manifest.getMainAttributes().getValue("Module-Class");
classes.add(moduleClassName);
urls.add(f.toURI().toURL());
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if(jarFile != null)
{
try
{
jarFile.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
return classes;
}
private static class ModuleFilter implements FileFilter {
@Override
public boolean accept(File file) {
return file.isFile() && file.getName().toLowerCase().endsWith(".jar");
}
}
//This function loads the jar file into the dalvik system
// retrieves the associated classes using its name
// and try to know if the loaded classes are implementing our interface
public static List<IPeripheral> loadModules(String folder, Context CurrentContext) {
List<IPeripheral> modules = new ArrayList<IPeripheral>();
List<String> classes = getModuleClasses(folder);
int index = 0;
for(String c : classes)
{
try
{
dalvik.system.PathClassLoader myClassLoader = new dalvik.system.PathClassLoader(urls.get(index).toString(), ModuleLoader.class.getClassLoader());
Class<?> moduleClass = Class.forName(c, true, myClassLoader);
//check and cast to an interface, then use it
if(IPeripheral.class.isAssignableFrom(moduleClass))
{
@SuppressWarnings("unused")
Class<IPeripheral> castedClass = (Class<IPeripheral>)moduleClass;
IPeripheral module = (IPeripheral)moduleClass.newInstance();
modules.add(module);
}
index++;
}
catch (ClassNotFoundException e1)
{
e1.printStackTrace();
}
catch (InstantiationException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}
return modules;
}
}
答案 1 :(得分:7)
使用ClassLoader而不是Dalvik路径类加载器也是一个好主意:
ClassLoader cl = new DexClassLoader(url, ApplicationConstants.ref_currentActivity.getFilesDir().getAbsolutePath(), null, ModuleList.class.getClassLoader());
其中url是您正在加载“from”的文件的位置。 ApplicationConstants.ref_currentActivity只是一个活动类 - 由于动态模块化加载,我的实现相当复杂 - 所以我需要以这种方式跟踪它 - 但是如果该类已经是一个活动,其他人可能只是使用“this”。 p>
在dalvik上使用类加载器的主要原因是它不需要将文件写入缓存,因此权限chmod 777 / data / dalvik-cache是不需要的 - 当然你也是不需要以编程方式从root用户的root上传递此命令。
最好不要让用户强行拔出手机,只是因为你的应用需要它。特别是如果你的应用程序是一个更专业的“意味着公司使用类型” - 。工作策略也反对使用有根电话。
如果有人对模块化装载有任何疑问 - 请随时询问。 我当前代码的基础都归功于Virginie Voirin以及我自己的修改。祝你好运!