Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can i Load a jar file dynamically in an android application (4.0.3)

Tags:

android

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; 
}
like image 779
Virginie Voirin Avatar asked Jul 12 '12 14:07

Virginie Voirin


People also ask

Can you run jar files on Android?

You cannot directly run jar files in android as android runs on Dalvik Virtual Machine , whereas to run jar files you will need Java Runtime Environment which has Java Virtual Machine .

What is a JAR file in Android?

A JAR (Java Archive) is a package file format typically used to aggregate many Java class files and associated metadata and resources (text, images, etc.) into one file to distribute application software or libraries on the Java platform.

How to install JAR files in Android Studio?

Step 1: Download any jar file as your requirement. For example, download the following three mail dependency JAR files for Android Studio. Please refer to this link to download those JAR files. Download all the three JAR files somewhere in the system. Step 2: Copy these three files and paste it into the libs folder as shown in the below image.

How to upload JAR files in dynamic mode?

For dynamic uploading of jar files, you can use my modification of URLClassLoader. This modification has no problem with changing the jar file during application operation, like the standard URLClassloader. All loaded jar files are loaded into RAM and thus independent of the original file.

What is the use of JAR file?

It does exactly that. You can install, uninstall, start and stop so called bundles, which are effectively JAR files. But it does a little more, as it offers e.g. services that can be dynamically discovered in JAR files at runtime. Or see the specification for the Java Module System.

Why is urlclassloader not working in Java 9?

With Java 9, the answers with URLClassLoader now give an error like: java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader This is because the class loaders used have changed.


1 Answers

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

}
like image 65
Virginie Voirin Avatar answered Oct 11 '22 19:10

Virginie Voirin