Android dalvik.system.PathClassLoader未加载多个JAR
我试图加载一组JAR文件,这些文件一起组成一个API。出于某种原因,我只能加载不依赖于其他JAR中定义的类。我开始怀疑Android类加载器根本无法处理从一个JAR文件到另一个JAR文件的接口实现。出于这个原因,我还将这些类解压到一个公共目录中,但是这也不起作用 请参阅以下代码。对于任何异常情况,我深表歉意,但我已尝试确保它在粘贴到名为MyProj的ADT项目中时能够直接编译Android dalvik.system.PathClassLoader未加载多个JAR,android,jar,classloader,Android,Jar,Classloader,我试图加载一组JAR文件,这些文件一起组成一个API。出于某种原因,我只能加载不依赖于其他JAR中定义的类。我开始怀疑Android类加载器根本无法处理从一个JAR文件到另一个JAR文件的接口实现。出于这个原因,我还将这些类解压到一个公共目录中,但是这也不起作用 请参阅以下代码。对于任何异常情况,我深表歉意,但我已尝试确保它在粘贴到名为MyProj的ADT项目中时能够直接编译 import java.io.File; import java.io.FileOutputStream; import
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import dalvik.system.PathClassLoader;
import android.content.Context;
// IPluginSource simply defines the method here at the top.
public class AndroidPluginSource implements IPluginSource
{
@Override
public void doSearching(ArrayList<ClassLoader> classLoaders, ArrayList<String> classNames)
{
String jarPaths = "";
// For each of the raw resources, JARs compiled into the 'res/raw' dir...
for (Field str : R.raw.class.getFields())
{
String resName = str.getName();
Logger.log(Level.FINE, "Resource: " + str);
try
{
// Copy the JAR file to the local FS.
InputStream is = MyProj.self.getResources().openRawResource(str.getInt(this));
OutputStream os = MyProj.self.openFileOutput(resName + ".jar", Context.MODE_PRIVATE);
copyData(is, os);
is.close();
os.close();
// Get JAR location.
String jarLoc = MyProj.self.getFilesDir() + File.separator + resName + ".jar";
// First attempt is just single classloaders, so we aren't suprised this won't work.
classLoaders.add(new PathClassLoader(jarLoc, MyProj.self.getClassLoader()));
//Logger.log(Level.FINE, " LOC: " + jarLoc);
// Keep running list of JAR paths, will that work?
if (jarPaths.length() > 0) jarPaths += File.pathSeparator;
jarPaths += jarLoc;
// We have to go through the JARs to get class names...
JarFile jar = new JarFile(jarLoc);
Enumeration<JarEntry> entries = jar.entries();
while (entries.hasMoreElements())
{
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
if (entryName.endsWith(".class"))
{
classNames.add(toClassName(entryName));
Logger.log(Level.FINE, " ENT: " + entryName);
// ...while we're here lets get the class out as a file.
String classLoc = MyProj.self.getFilesDir() + File.separator + entryName;
Logger.log(Level.FINER, " CLS: " + classLoc);
File classFile = new File(classLoc);
classFile.delete();
classFile.getParentFile().mkdirs();
InputStream jis = jar.getInputStream(entry);
//OutputStream jos = MyProj.self.openFileOutput(classLoc, Context.MODE_PRIVATE);
OutputStream jos = new FileOutputStream(classFile);
copyData(jis, jos);
jos.close();
jis.close();
}
}
}
catch (Exception ex)
{
Logger.log(Level.SEVERE, "Failed plugin search", ex);
}
}
File f = MyProj.self.getFilesDir();
recursiveList(f, 0);
// So we have a class loader loading classes...
PathClassLoader cl = new PathClassLoader(VermilionAndroid.self.getFilesDir().getAbsolutePath(), ClassLoader.getSystemClassLoader());
classLoaders.add(cl);
// A JAR loader loading all the JARs...
PathClassLoader jl = new PathClassLoader(jarPaths, ClassLoader.getSystemClassLoader());
classLoaders.add(jl);
// And if edited as below we also have a DexLoader and URLClassLoader.
}
// This is just so we can check the classes were all unpacked together.
private void recursiveList(File f, int indent)
{
StringBuilder sb = new StringBuilder();
for (int x = 0; x < indent; x++) sb.append(" ");
sb.append(f.toString());
Logger.log(Level.INFO, sb.toString());
File[] subs = f.listFiles();
if (subs != null)
{
for (File g : subs) recursiveList(g, indent+4);
}
}
// Android helper copy file function.
private void copyData(InputStream is, OutputStream os)
{
try
{
int bytesRead = 1;
byte[] buffer = new byte[4096];
while (bytesRead > 0)
{
bytesRead = is.read(buffer);
if (bytesRead > 0) os.write(buffer, 0, bytesRead);
}
}
catch (Exception ex) {}
}
// Goes from a file name or JAR entry name to a full classname.
private static String toClassName(String fileName)
{
// The JAR entry always has the directories as "/".
String className = fileName.replace(".class", "").replace(File.separatorChar, '.').replace('/', '.');
return className;
}
}
…这将导致一个新的异常-UnsupportdOperationException:无法加载此类类文件
以及:
也没有正常工作,但感谢您的建议Peter Knego
我应该澄清,在JAR文件中,我有:
JAR1:
public interface IThing
public class ThingA implements IThing
JAR2:
public class ThingB implements IThing
我不完全确定您想做什么,但我怀疑Java语言对类装入器的定义不支持您想要的 类装入器按层次结构排列。如果您向类加载器CL1请求类Foo的副本,它将询问其父级是否知道Foo是什么。这将沿着链向上到达引导加载程序,当事情失败时,它将返回,直到最终CL1有机会找到一个副本。(它不必以这种方式工作,而且有一个测试用例故意将它“弄错”,但它几乎总是这样做的。) 假设CL1定义了Foo。Foo实现了IBar,因此在准备该类时,VM将要求CL1查找IBar。通常的搜索已经完成 如果在CL2中定义了IBar,并且CL2不是CL1的父级,那么CL1中的任何内容都无法看到IBar 因此,如果要为各种jar文件创建一组“对等”类装入器,就不能在它们之间直接引用类 这不是Android独有的
如果您创建一个类加载器并将整个JAR集放入路径中,您可以根据需要进行混合和匹配。这个问题没有解决方案,到目前为止,我已经解决了。请看一看:抱歉,您完全错了。这正是该项目目前在PC上运行的方式…再次抱歉,我重读了你的文章,你在最后一段中所说的正是我试图解决的问题——用上面列出的四种不同的方法……我还尝试将类加载器链接在一起作为孩子和家长。上面列出的最后一次尝试的失败是什么?(设备没有/tmp目录,所以我想确保它不会因此而失败。)失败类似于“您无法加载那种类型的类文件”。我知道二进制不兼容必须在某个地方发生!:-)
URLClassLoader ul = null;
try
{
URL[] contents = new URL[jarURLs.size()];
ul = new URLClassLoader(jarURLs.toArray(contents), ClassLoader.getSystemClassLoader());
}
catch (Exception e) {}
classLoaders.add(ul);
DexClassLoader dl = new DexClassLoader(jarPaths, "/tmp", null, getClass().getClassLoader());
classLoaders.add(dl);
JAR1:
public interface IThing
public class ThingA implements IThing
JAR2:
public class ThingB implements IThing