dalvik.system.PathClassLoader не загружает несколько JAR-файлов - PullRequest
1 голос
/ 15 февраля 2011

Я пытаюсь загрузить набор файлов JAR, которые собираются вместе для создания API.По какой-то причине я могу загружать только классы, не зависящие от определений в других JAR-файлах.Я начинаю подозревать, что загрузчики классов Android просто не обрабатывают реализацию интерфейса из одного файла JAR в другой.По этой причине я также распаковал классы в общий каталог, однако это тоже не работает.

Пожалуйста, смотрите следующий код.Извиняюсь за любые аномалии, но я попытался гарантировать, что он будет компилироваться прямо, если вставить в проект ADT под названием MyProj.

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;
    }   
}

Следующий код - то, откуда это вызывается.*

Спасибо за вашу помощь.

РЕДАКТИРОВАТЬ: Я также попытался добавить

    URLClassLoader ul = null;
    try 
    {
        URL[] contents = new URL[jarURLs.size()];
        ul = new URLClassLoader(jarURLs.toArray(contents), ClassLoader.getSystemClassLoader());
    } 
    catch (Exception e) {} 
    classLoaders.add(ul);        

..., что вызывает новое исключение - UnsupportedOperationException: не удается загрузить этот типфайла класса.

AND:

    DexClassLoader dl = new DexClassLoader(jarPaths, "/tmp", null, getClass().getClassLoader());
    classLoaders.add(dl);

Также не работает правильно, но спасибо за предложение Peter Knego

Я должен уточнитьчто в файлах JAR у меня есть:

JAR1:
    public interface IThing
    public class ThingA implements IThing

JAR2:
    public class ThingB implements IThing

Ответы [ 2 ]

1 голос
/ 22 июня 2011

У этой проблемы не было решения, я до сих пор обходил ее.

1 голос
/ 16 февраля 2011

Я не совсем уверен, что вы пытаетесь сделать, но я подозреваю, что то, что вы хотите, не поддерживается определением загрузчика классов в языке Java.

Загрузчики классов расположены в иерархии,Если вы запрашиваете у загрузчика классов CL1 копию класса Foo, он спросит своего родителя, знает ли он, что такое Foo.Это идет по цепочке к загрузчику начальной загрузки, и когда что-то не получается, оно возвращается обратно, пока в конечном итоге CL1 не получит шанс выйти и найти копию.(У него нет для работы таким образом, и есть тестовый пример, который намеренно делает это "неправильно", но почти всегда это делается так.)

Предположим, CL1 действительно определяет Foo,Foo реализует IBar, поэтому при подготовке этого класса виртуальная машина попросит CL1 найти IBar.Обычный поиск выполняется.

Если IBar определен в CL2, а CL2 не является родительским для CL1, то ничто в CL1 не сможет видеть IBar.

Так что если высоздавая группу «одноранговых» загрузчиков классов для различных jar-файлов, вы не можете напрямую ссылаться на классы между ними.

Это не уникально для Android.

Если вы создаете один загрузчик классови поместите весь набор банок в путь, вы можете смешивать и сочетать, как вы хотите.

...