2012-07-12 8 views
15

Mam aplikację na Androida, która musi ładować dynamicznie klasę, niezdefiniowaną liczbę klasy jar, która zaimplementowała interfejs.Jak mogę załadować plik jar dynamicznie w aplikacji na Androida (4.0.3)

W rzeczywistości patrzę na katalog i wyświetlam wszystkie pliki jar, które znajdują się w tym katalogu. Otwieram manifest pliku jar i znajduję skojarzoną klasę i wyświetlam je. Po dodaniu elementu dexClassLoader załadowałem wszystkie pliki jar i sprawdziłem, czy klasy, które znalazłem w interfejsie manisfest implementują mój interfejs. W ten sposób mogę mieć całą klasę, która zaimplementowała mój interfejs, nie znając ich na początku. Aby kontynuować, mam listę słoików klas, które implementują mój interfejs, ale lista jest nieznana przez moją aplikację Android i przeze mnie. Lista klasy słoików może być zmieniana za każdym razem, gdy uruchamiam moją aplikację.

Ale kiedy próbowałem utworzyć DexClassLoader, nie udało się. Zawsze mam wskaźnik zerowy

Do mojego testu użyłem emulatora. Mam skopiowane z moich DDMS pliki jar do katalogu /data/data/com.example.Myappli/JarFilesDirectory/*.jar

Zauważ, że moje zawartość pliku dek

pliku JAR dużo czytam o tym. Niektóre problemy z pozwoleniami Próbowałem wszystkich rzeczy, ale nie znalazłem rozwiązania

Czy ktoś może mi pomóc, proszę!

tu treść manifestu pliku jar

Manifest-Version: 1.0

Module-Klasa: com.example.asktester.AskPeripheral

Oto mój kod:

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

Odpowiedz

16

znalazłem rozwiązanie do mojego wydania.

Aby załadować dynamicznie jar, klasy, które implementują interfejs w android aplikacji, niektóre prace muszą być wykonane w słoiku:

  • Stwórz własną manisfest do słoika i umieścić tę informację

    Manifest-Version: 1.0 
    Module-Class: com.example.myjar.MyPeripheral 
    
  • Export Twój słoik przy użyciu eclipse i umieścić w parametrze, że wykorzystuje własną manisfest

  • Stworzy classes.dex powiązany słoika (ten plik jest potrzebny przez Dalvik VM, po prostu słoik może nie być odczytywane przez Dalvik VM)

    dx --dex --output=C:\classes.dex C:\MyJar.jar 
    

należy zachować ostrożność, nazwa pliku dex MUSI BYĆ klas.dex

  • Dodaj classes.dex pliku w pliku jar

    aapt add C:\MyJar.jar C:\classes.dex 
    
  • Trzeba także mieć prawo zapisu do katalogu cache Dalvik

    adb shell chmod 777 /data/dalvik-cache 
    

    Do it za każdym razem ponownie uruchom swój emulator

  • Ten plik jar w emulatorze na przykład na SDcard

  • Użyj PathClassLoader załadować pliku jar

    dalvik.system.PathClassLoader myClassLoader = new dalvik.system.PathClassLoader("/Sdcard/MyJar.jar", ModuleLoader.class.getClassLoader()); 
    

NB: LogCat w Eclipse daje cenne informacje. Nie zapomnij spojrzeć na jego wiadomości

Poniżej kod:

Mój interfejs:

package com.example.StandartPeripheral; 

public interface IPeripheral { 

    public boolean Initialize(); 
    public boolean configure(); 
    public boolean execute(); 
    public String GetName(); 
} 

MyPeripheral który implementuje interfejs

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

} 

Jak ładuj dynamicznie pliki 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; 
    } 

} 
6

Byłoby również dobry pomysł, aby używać ClassLoader zamiast ładowarki klasy ścieżka Dalvik:

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

Gdzie URL jest lokalizacja pliku ładowanego „z”. ApplicationConstants.ref_currentActivity to po prostu klasa aktywności - moja implementacja jest dość skomplikowana ze względu na dynamiczne modularne ładowanie - więc musiałem to śledzić w ten sposób - ale inni mogą prawdopodobnie po prostu użyć "tego", jeśli ta klasa jest już aktywnością.

GŁÓWNYM powodem używania modułu ładującego klasy przez dalvik jeden - jest to, że nie wymaga zapisywania plików w pamięci podręcznej, a zatem uprawnienie chmod 777/data/dalvik-cache jest niepotrzebne - i oczywiście ty również nie musiałby pro gramatycznie przekazywać tego polecenia z katalogu głównego na zrootowanym telefonie.

Zawsze najlepiej jest nie zmuszać użytkowników do rootowania swoich telefonów, ponieważ aplikacja tego wymaga. Zwłaszcza jeśli twoja aplikacja jest bardziej profesjonalna "przeznaczona do użytku przez firmę" -Wykonują również zasady dotyczące używania zrootowanych telefonów.

Jeśli ktoś ma jakiekolwiek pytania na temat obciążenia modułowego - proszę zapytać. Podstawa mojego obecnego kodu to zasługa Virginie Voirin wraz z moimi własnymi modyfikacjami. Powodzenia wszystkim!

Powiązane problemy