Android plugin (2): Use DexClassLoader to dynamically load the apk in assets

Source: Internet
Author: User

Android plugin (2): Use DexClassLoader to dynamically load the apk in assets
As mentioned in the previous blog, we can use MultiDex. java to load offline apk files. Note that the class in the apk is loaded to the current PathClassLoader. If there are too many apk files, ANR may occur. So, can we use DexClassLoader to load the apk? Of course! First, take a look at the Doc documentation.

A class loader that loads classes from .jar and .apk files containing a classes.dex entry. This can be used to execute code not installed as part of an application.

That is to say, DexClassLoader can load a compressed package containing the classes. dex file, either jar or apk. So what should I pay attention to when loading an offline apk file?

1. DexClassLoader construction method:
DexClassLoader (String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)

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

After learning about the above two points, we can dynamically load the apk in assets based on the parameters required by DexClassLoader.

Source code BundleClassLoaderManager

This class is mainly responsible for managing these DexClassLoader. First, we define a class called BundleDexClassLoader, which inherits from DexClassLoader and is used to load offline apk files. Each apk file corresponds to a BundleDexClassLoader, while BundleClassLoaderManager stores a List for searching classes during loading. The Code is as follows:
 

Package net. mobctrl. hostapk; import java. io. file; import java. io. filenameFilter; import java. util. arrayList; import java. util. list; import android. annotation. targetApi; import android. content. context; import android. OS. build;/*** @ Author Zheng Haibo * @ PersonalWebsite http://www.mobctrl.net * @ version $ Id: BundleClassLoaderManager. java, v 0.1 December 11, 2015 7:30:59 * mochuan. zhb Exp $ * @ Description */@ TargetApi (Build. VERSION_CODES.ICE_CREAM_SANDWICH) public class BundleClassLoaderManager {public static List
  
   
BundleDexClassLoaderList = new ArrayList
   
    
();/*** Load the apk file in Assets * @ param context */public static void install (Context context) {AssetsManager. copyAllAssetsApk (context); // obtain the dex File list File dexDir = context. getDir (AssetsManager. APK_DIR, Context. MODE_PRIVATE); File [] szFiles = dexDir. listFiles (new FilenameFilter () {@ Override public boolean accept (File dir, String filename) {return filename. endsWith (AssetsManager. FILE_FILTER) ;}}); for (File f: szFiles) {System. out. println ("debug: load file:" + f. getName (); BundleDexClassLoader bundleDexClassLoader = new BundleDexClassLoader (f. getAbsolutePath (), dexDir. getAbsolutePath (), null, context. getClassLoader (); bundleDexClassLoaderList. add (bundleDexClassLoader);}/*** Search Class ** @ param className * @ return * @ throws ClassNotFoundException */public static Class
    LoadClass (Context context, String className) throws ClassNotFoundException {try {Class
    Clazz = context. getClassLoader (). loadClass (className); if (clazz! = Null) {System. out. println ("debug: class find in main classLoader"); return clazz ;}} catch (Exception e) {e. printStackTrace ();} for (BundleDexClassLoader bundleDexClassLoader: bundleDexClassLoaderList) {try {Class
    Clazz = bundleDexClassLoader. loadClass (className); if (clazz! = Null) {System. out. println ("debug: class find in bundle classLoader"); return clazz ;}} catch (Exception e) {e. printStackTrace () ;}} throw new ClassCastException (className + "not found exception ");}}
   
  

Note:

1. install Method
The install method is to copy all the apks in assets to the private directory, traverse the private directory, use BundleDexClassLoader to load the apk file, and then save these BundleDexClassLoader to the array.

2. loadClass Method
This method first finds the required class from the current ClassLoader. If it cannot be found, it traverses the search from the List.

Run DEMO

In MainActivity, we can call the method in the apk class in the following way:

Private void loadApk () {try {Class
  Clazz = BundleClassLoaderManager. loadClass (getApplicationContext (), "net.mobctrl.normal.apk. Utils"); Constructor
  Constructor = clazz. getConstructor (); Object bundleUtils = constructor. newInstance (); Method printSumMethod = clazz. getMethod ("printSum", Context. class, int. class, int. class, String. class); printSumMethod. setAccessible (true); Integer sum = (Integer) printSumMethod. invoke (bundleUtils, getApplicationContext (), 10, 20, "calculation result"); System. out. println ("debug: sum =" + sum);} catch (ClassNotFoundException e) {e. printStackTrace ();} catch (SecurityException e) {e. printStackTrace ();} catch (NoSuchMethodException e) {e. printStackTrace ();} catch (IllegalArgumentException e) {e. printStackTrace ();} catch (InstantiationException e) {e. printStackTrace ();} catch (IllegalAccessException e) {e. printStackTrace ();} catch (InvocationTargetException e) {e. printStackTrace ();}}

Unlike MultiDex, we load classes through BundleClassLoaderManager, rather than the current ClassLoader.

Improvement Plan

Just like the loadClass method in BundleClassLoaderManager, we can create a ClassLoader object by rewriting the findClass method of the current ClassLoader. Then, in the findClass method of Override, We can first find the class from the current ClassLoader, then, we can traverse and search from BundleDexClassLoader, so that we can call the classes in Bundle in the Host project or the classes in the Host.

       mClassLoader = new ClassLoader(super.getClassLoader()) {            @Override            protected Class
   findClass(String className)                    throws ClassNotFoundException {                Class clazz = BundleClassLoaderManager.loadClass(context,className);                if (clazz == null) {                    throw new ClassNotFoundException(className);                }                return clazz;            }        };
Summary

Both the previous blog and this blog will load classes. If all the classes to be loaded are tool classes and resources do not need to be loaded, the above solution will be free of problems. However, if the loaded class is a Fragment or Activity UI and the resource file needs to be referenced, how can this problem be solved?

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.