У меня есть приложение для Android, которое должно загружать динамически класс, неопределенное число класса 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
Обратите внимание, что мой файл 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; }
Я нашел решение своей проблемы.
Чтобы загрузить динамически jar, классы, которые реализуют интерфейс в приложении Android, необходимо выполнить некоторые задания в банке:
Создайте свой собственный manessest для банки и поместите эту информацию
Manifest-Version: 1.0 Module-Class: com.example.myjar.MyPeripheral
Экспортируйте свою банку с помощью eclipse и введите параметр, который использует свой собственный manessest
Создайте class.dex, связанный с jar (этот файл необходим Dalvik VM, просто jar не может быть прочитан dalvik VM)
dx --dex --output=C:\classes.dex C:\MyJar.jar
Будьте осторожны, имя файла dex ДОЛЖНО БЫТЬ class.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; } }
Также было бы неплохо использовать ClassLoader, а не загрузчик класса пути Dalvik:
ClassLoader cl = new DexClassLoader(url, ApplicationConstants.ref_currentActivity.getFilesDir().getAbsolutePath(), null, ModuleList.class.getClassLoader());
Где url – это местоположение файла, который вы загружаете «из». ApplicationConstants.ref_currentActivity – это просто класс активности – моя реализация довольно сложна из-за динамической модульной загрузки – поэтому мне нужно было отслеживать ее таким образом, но другие, возможно, просто используют «это», если этот класс уже является активностью.
Причина MAIN для использования загрузчика классов по сравнению с dalvik – заключается в том, что он не требует, чтобы файлы записывались в кеш, и поэтому разрешение chmod 777 / data / dalvik-cache не требуется – и, конечно же, вы также не захотите Необходимо пропустить эту команду из корневого каталога на корневом телефоне.
Всегда лучше не заставлять пользователей заставлять их использовать, просто потому, что это требует ваше приложение. Особенно, если ваше приложение является более профессиональным «ориентированным на компанию», – обычно применяются правила работы с использованием корневых телефонов.
Если у кого-то есть вопросы по модульной загрузке, пожалуйста, не стесняйтесь спрашивать. Основа моего текущего кода – все благодаря Вирджини Войрин, а также мои собственные модификации. Всем удачи!