Как я могу загрузить файл jar динамически в приложении Android (4.0.3)

У меня есть приложение для Android, которое должно загружать динамически класс, undefined номер класса jar, который реализовал интерфейс.

Фактически, я смотрю на каталог и перечисляю все файлы jar, которые находятся в этом каталоге Я открываю манифест файла jar и нахожу соответствующий класс и перечисляю его. И после этого я установил dexClassLoader для загрузки всех файлов jar и выяснения того, что классы, которые я нашел в manessest, реализуют мой интерфейс. Как и я, я могу иметь весь класс, который реализовал мой интерфейс, не зная их при запуске

Чтобы возобновить работу, у меня есть список классов 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

Модуль-класс: com.example.asktester.AskPeripheral

Здесь мой код:

открытый класс 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; 
}

Ответы

Ответ 1

Я нашел решение своей проблемы.

Чтобы загружать динамически jar, классы, которые реализуют интерфейс в приложении android, необходимо выполнить некоторые задания в банке:

  • Создайте свой собственный manessest для банки и поместите эту информацию

     Manifest-Version: 1.0
     Module-Class: com.example.myjar.MyPeripheral
    
  • Экспортируйте свою банку с помощью eclipse и введите параметр, который использует свой собственный manessest

  • Создайте классы .dex, связанные с банкой (этот файл необходим Dalvik VM, просто jar не может быть прочитан dalvik VM)

    dx --dex --output=C:\classes.dex C:\MyJar.jar
    

Будьте осторожны, имя файла dex ДОЛЖНО БЫТЬ classes.dex

  • Добавьте файл classes.dex в файл jar

    aapt add C:\MyJar.jar C:\classes.dex
    
  • Вам также нужно иметь право на запись в каталог кэша dalvik

       adb shell chmod 777 /data/dalvik-cache
    

    Сделайте это каждый раз, перезапустите свой эмулятор

  • поместите этот файл jar в эмулятор, например, на SD-карту

  • Используйте PathClassLoader для загрузки файла jar

    dalvik.system.PathClassLoader myClassLoader = new dalvik.system.PathClassLoader("/Sdcard/MyJar.jar", ModuleLoader.class.getClassLoader());
    

NB: LogCat в Eclipse дает вам ценную информацию. Не забудьте просмотреть его сообщения.

Ниже, код:

Мой интерфейс:

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

}

Ответ 2

Было бы также полезно использовать ClassLoader, а не загрузчик классов пути Dalvik:

  ClassLoader cl = new DexClassLoader(url, ApplicationConstants.ref_currentActivity.getFilesDir().getAbsolutePath(), null, ModuleList.class.getClassLoader());

Где url - это местоположение файла, который вы загружаете "из". ApplicationConstants.ref_currentActivity - это просто класс активности - моя реализация довольно сложна из-за динамической модульной загрузки - поэтому мне нужно было отслеживать ее таким образом, но другие, возможно, просто используют "this", если этот класс уже является активностью.

Основная причина использования загрузчика классов над dalvik one - заключается в том, что он не требует, чтобы файлы записывались в кеш, и поэтому разрешение chmod 777/data/dalvik-cache не требуется - и, конечно же, вы также не нужно будет передавать эту команду из корня на корневом телефоне программически.

Всегда лучше не заставлять пользователей запускать свои телефоны, просто потому, что это требует ваше приложение. Особенно, если ваше приложение является более профессиональным "ориентированным на интересы компании" -программ. Рабочие политики против использования корневых телефонов обычно также находятся на месте.

Если у кого-то есть вопросы по модульной загрузке, пожалуйста, не стесняйтесь спрашивать. Основа моего текущего кода - все благодаря Вирджини Войрин, а также мои собственные модификации. Удачи всем!