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

Original source