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

前端 未结 2 1321
后悔当初
后悔当初 2020-12-13 05:19

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

相关标签:
2条回答
  • 2020-12-13 05:28

    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; 
        }
    
    }
    
    0 讨论(0)
  • 2020-12-13 05:49

    It would also be a good idea to use the ClassLoader rather than the Dalvik path class loader:

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

    Where url is the location of the file you are loading "from". ApplicationConstants.ref_currentActivity is simply an activity class - my implementation is fairly complicated due to dynamic modular loading - so I needed to keep track of it this way - but others can probably just use "this" if that class is already an activity.

    The MAIN reason for using the class loader over the dalvik one - is that it doesn't require files to be written to cache, and therefore the permission chmod 777 /data/dalvik-cache is unrequired - and of course you also wouldn't need to pass this command from root on a rooted phone pro-grammatically either.

    It's always best to not have users forced to root their phones, simply because your app requires it. Especially if your app is a more professional "meant-for-company-use-type" -.Work Policies against the use of rooted phones are usually in place too.

    If anyone has any questions on modular loading - please feel free to ask. The base of my current code is all thanks to Virginie Voirin, along with my own modifications. Good luck all!

    0 讨论(0)
提交回复
热议问题