How can i Load a jar file dynamically in an android application (4.0.3)
android
Solution
I found the solution to my issue.
To load dynamically jar, classes which implement an interface in an android application, some jobs need to be done in the jar :
Create your own manisfest for the jar and put this information
Manifest-Version: 1.0
Module-Class: com.example.myjar.MyPeripheral
Export your jar using eclipse and put in parameter that it uses its own manisfest
Create the classes.dex associated to the jar (this file is needed by the Dalvik VM, simply jar can not be read by the dalvik VM)
dx --dex --output=C:\classes.dex C:\MyJar.jar
Be carefull, the name of the dex file MUST BE classes.dex
Add the file classes.dex in the jar file
aapt add C:\MyJar.jar C:\classes.dex
You need also to have the right to write into the dalvik cache directory
adb shell chmod 777 /data/dalvik-cache
Do it each time, your relaunch your emulator
put this jar file into the emulator for example on the SDcard
Use a PathClassLoader to load the jar file
dalvik.system.PathClassLoader myClassLoader = new dalvik.system.PathClassLoader("/Sdcard/MyJar.jar", ModuleLoader.class.getClassLoader());
NB : the LogCat in Eclipse gives you precious information. Do not forget to look at its messages
Below, the code :
My interface :
package com.example.StandartPeripheral;
public interface IPeripheral {
public boolean Initialize();
public boolean configure();
public boolean execute();
public String GetName();
}
MyPeripheral which implements the interface
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;
}
}
How to load dynamically the jar files
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;
}
}
Problem
I have an android application which has to load dynamically class ,an undefined number of a jar class which implemented an interface. In fact, I look at a directory and list all the jar files which are in this directory I open the manifest of the jar file and find the associated class and list them. And after, i instancied a dexClassLoader to load all the jar files and to find if the classes i have found in the manisfest implement my interface. Like this I can have all the class which implemented my interface without knowing them at the begginning To resume, i have a list of class jar which implement my interface but the list is unknown by my android application and by me. The list of jar class can changed each time i launch my application. But when i tried to create the DexClassLoader it is failed. I have always a null pointer ``` DexClassLoader classLoader = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),dexOutputDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader()); ``` To make my test i used the emulator. I have copied with my DDMS the jar files into the directory /data/data/com.example.Myappli/JarFilesDirectory/*.jar Notice that my jar file contents the dex file I read a lot of thing about this. Some permissions issues I have tried every thing but not found the solution Can someone help me please !!! here the content of a manifest of a jar file Manifest-Version: 1.0 Module-Class: com.example.asktester.AskPeripheral Here my code : 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; } ```