Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I load a class from a jar file packaged in my .apk file?

I am trying to load a plugin implementation of an interface from a jar file which is in the /assets directory of my .apk file. The only way I've been able to get this to work is by extracting the jar file to private external storage and then passing that file to the DexClassLoader.

That works, but why should the jar have to exist in two places (the .apk and private external storage)? The DexClassLoader has to have a file path as its argument.

Is there a way to give it a direct path to the file that is in the /assets folder so that I don't have to use up external storage for an extra copy of what's already present?

Here are the relevant code snippets:

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

and

/** 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();
      }
    }
  }
}

and

/** 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();
  }
}
like image 624
Chuck Krutsinger Avatar asked May 03 '12 21:05

Chuck Krutsinger


1 Answers

Is there a way to give it a direct path to the file that is in the /assets folder so that I don't have to use up external storage for an extra copy of what's already present?

The answer is No. I suppose you follow this blog posted by official source implementing your code. if there is a better way of doing things, the bloger should recommend it in his blog.

Reason why you need optimizedDirectory is explained in the API:

This class loader requires an application-private, writable directory to cache optimized classes.

Also note that assets directory is not writable in apk, so it can't be done with purely assets directory.

Reason why you need copy jar file is a little bit subtle, mentioned in the blog:

First, it has to be copied to a storage location whose path can be supplied to the class loader.

Everything (folders/files) embedded within apk archive is not exposable (or interpretable) to the underlying file system at runtime. In another word, dexPath required in both DexClassLoader and PathClassLoader's constructor need a solid path string like /data/data/com.example/dex/common-lib.jar that represents the file in file system.

like image 138
yorkw Avatar answered Sep 20 '22 13:09

yorkw