¿Cómo puedo cargar una clase desde un archivo jar empaquetado en mi archivo .apk?

Estoy intentando cargar una implementación de complemento de una interfaz desde un archivo jar que se encuentra en el directorio /assets de mi archivo .apk. La única forma en que he podido hacer que esto funcione es extrayendo el archivo jar a un almacenamiento externo privado y luego pasando ese archivo a DexClassLoader.

Eso funciona, pero ¿por qué el jar tiene que existir en dos lugares (el .apk y el almacenamiento externo privado)? El DexClassLoader debe tener una ruta de archivo como argumento.

¿Hay alguna manera de darle una ruta directa al archivo que está en la carpeta /assets para que no tenga que usar el almacenamiento externo para obtener una copia adicional de lo que ya está presente?

Aquí están los fragmentos de código relevantes:

// somewhere in my main Activity ...
  final File aExtractedDexFile = new File(getDir("dex", Context.MODE_PRIVATE),
                                  LIBRARY_DEX_JAR);
  extractDexTo(aExtractedDexFile);
  loadLibraryProvider(aExtractedDexFile);

y

/** Extract the jar file that contains the implementation class.dex and place in private storage */
private void extractDexTo(File tJarInternalStoragePath) {
  BufferedInputStream aJarInputStream = null;
  OutputStream aDexOutputStream = null;

  try {
    aJarInputStream = new BufferedInputStream(getAssets().open(LIBRARY_DEX_JAR));
    aJarOutputStream = new BufferedOutputStream(new FileOutputStream(tJarInternalStoragePath));
    byte[] buf = new byte[BUF_SIZE];
    int len;
    while ((len = aJarInputStream.read(buf, 0, BUF_SIZE)) > 0)
    {
      aJarOutputStream.write(buf, 0, len);
    }
    aJarOutputStream.close();
    aJarInputStream.close();
  } catch (IOException e) {
    if (aDexOutputStream != null) {
      try {
        aJarOutputStream.close();
      } catch (IOException ioe) {
        ioe.printStackTrace();
      }
    }

    if (aJarInputStream != null) {
      try {
        aJarInputStream.close();
      } catch (IOException ioe) {
        ioe.printStackTrace();
      }
    }
  }
}

y

/** Use DexClassLoader to load the classes from LibraryProvider */
private void loadLibraryProvider(File tFile) {
  // Internal storage where the DexClassLoader writes the optimized dex file to.
  final File aOptimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);

  // Initialize the class loader with the secondary dex file.
  DexClassLoader cl = new DexClassLoader(tFile.getAbsolutePath(),
          aOptimizedDexOutputPath.getAbsolutePath(),
          null,
          getClassLoader());
  Class<?> aLibProviderClazz = null;

  try {
    // Load the library class from the class loader.
    aLibProviderClazz = cl.loadClass(LIBRARY_PROVIDER_CLASS);      
    sLibraryProvider = (LibraryInterface) aLibProviderClazz.newInstance();
  } catch (Exception exception) {
    // Handle exception gracefully here.
    exception.printStackTrace();
  }
}

preguntado el 03 de mayo de 12 a las 22:05

Esta puede ser una pregunta estúpida, pero ¿por qué empaquetar Jar en assets/ en lugar de libs/ donde se puede hacer referencia directamente como parte de la ruta de compilación de aplicaciones? -

Se necesitaría mucho contexto para explicarlo, pero la respuesta más corta que puedo dar es que estoy brindando un marco que otros usarán. Sus contribuciones se compilarán en frascos que se empaquetarán con el marco para formar una aplicación completa. El marco tendrá una interfaz para cargar los módulos desde el jar en tiempo de ejecución. -

¿Has probado file:///android_asset/...? Además, considere estas discusiones: stackoverflow.com/questions/4789325/… stackoverflow.com/questions/4820816/… -

@ArunWarrier La solución anterior es la forma correcta de hacerlo. Vea la respuesta a continuación. Debe extraer el archivo jar en un directorio antes de entregarlo al cargador de clases. -

@AndEngine sí, exactamente. -

1 Respuestas

¿Hay alguna manera de darle una ruta directa al archivo que está en la carpeta /assets para que no tenga que usar el almacenamiento externo para obtener una copia adicional de lo que ya está presente?

La respuesta es No. Supongo que sigues este blog publicado por fuente oficial implementando su código. si hay una forma mejor de hacer las cosas, el bloguero debería recomendarla en su blog.

Razón por la que necesitas directorio optimizado se explica en la API:

Este cargador de clases requiere un directorio grabable privado de la aplicación para almacenar en caché las clases optimizadas.

También tenga en cuenta que el directorio de activos no se puede escribir en apk, por lo que no se puede hacer con el directorio de activos puramente.

La razón por la que necesita copiar el archivo jar es un poco sutil, mencionada en el blog:

Primero, debe copiarse en una ubicación de almacenamiento cuya ruta se pueda proporcionar al cargador de clases.

Todo (carpetas/archivos) incrustado dentro del archivo apk no se puede exponer (o interpretar) al sistema de archivos subyacente en tiempo de ejecución. En otras palabras, dexPath requerido tanto en DexClassLoader como en el constructor de PathClassLoader necesita una cadena de ruta sólida como /data/data/com.example/dex/common-lib.jar que representa el archivo en el sistema de archivos.

Respondido el 20 de junio de 20 a las 10:06

Tiene toda la razón sobre la documentación de la API. Me había perdido eso durante mi lectura. Es absolutamente necesario copiar el .jar del archivo .apk. Había leído en otra parte que es vital que se copie en un directorio privado para evitar un ataque de inyección de código. - Chuck Krutsinger

Además, para cualquiera que siga este tema. El artículo del blog que me mostró cómo cargar un frasco android-developers.blogspot.co.nz/2011/07/… dice que debes usar ant desde la línea de comando no es del todo exacto. Pude reproducir todos sus trucos de hormigas usando Eclipse agregando un archivo build.xml personalizado. Hay un excelente tutorial sobre cómo usar ant con Eclipse en ibm.com/developerworks/opensource/tutorials/os-ecl-easyant/… - Chuck Krutsinger

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.