Android Plugin (ii): Using Dexclassloader to dynamically load APK introduction in assets
In a previous blog post, we can use Multidex.java to load an offline APK file. It is important to note that the classes in the APK are loaded into the current pathclassloader, and if there are too many apk files, the ANR situation may occur. So, can we use Dexclassloader to load the APK? Of course you can! First 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.
In other words, dexclassloader can load a compressed package containing classes.dex files, either as a jar or as an apk. So what do I need to be aware of when loading an offline APK file?
-
1.DexClassLoader Method of Construction:
Dexclassloader (String Dexpath, String optimizeddirectory, String LibraryPath, ClassLoader parent)
-
2. Private Directory
This class loader requires a application-private, writable directory to cache optimized classes.
Knowing the above two points, we can dynamically load the APK in the assets according to the parameters required by Dexclassloader.
Source
Bundleclassloadermanager
This class is primarily responsible for managing these dexclassloader, first, we define a class called Bundledexclassloader, which inherits from Dexclassloader, which is used to load the offline APK file. Each apk file corresponds to a bundledexclassloader, and Bundleclassloadermanager saves a list, which is used to find the class when it is loaded. The specific code is as follows:
Packagenet.mobctrl.hostapk;ImportJava.io.File;ImportJava.io.FilenameFilter;ImportJava.util.ArrayList;ImportJava.util.List;ImportAndroid.annotation.TargetApi;ImportAndroid.content.Context;ImportAndroid.os.Build;/** * @Author Zheng haibo * @PersonalWebsite http://www.mobctrl.net * @version $Id: Bundle Classloadermanager.java, v 0.1 December 11, 2015 PM 7:30:59 * MOCHUAN.ZHB EXP $ * @Description */@TargetApi(Build.version_codes. Ice_cream_sandwich) Public class bundleclassloadermanager { Public StaticList<bundledexclassloader> bundledexclassloaderlist =NewArraylist<bundledexclassloader> ();/** * Load APK file in assets * @param Context * * Public Static void Install(Context context) {assetsmanager.copyallassetsapk (context);//Get a list of Dex filesFile Dexdir = Context.getdir (Assetsmanager.apk_dir, context.mode_private); file[] Szfiles = Dexdir.listfiles (NewFilenameFilter () {@Override Public Boolean Accept(File dir, String filename) {returnFilename.endswith (Assetsmanager.file_filter); } }); for(File f:szfiles) {System.out.println ("Debug:load file:"+ F.getname ()); Bundledexclassloader Bundledexclassloader =NewBundledexclassloader (F.getabsolutepath (), Dexdir.getabsolutepath (),NULL, Context.getclassloader ()); Bundledexclassloaderlist.add (Bundledexclassloader); } }/** * Find class * * @param className * @return * @throws classnotfoundexcept Ion * / Public StaticClass<?>LoadClass(Context context,string ClassName)throwsclassnotfoundexception {Try{class<?> clazz = Context.getclassloader (). LoadClass (ClassName);if(Clazz! =NULL) {System.out.println ("Debug:class Find in main ClassLoader");returnClazz; } }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");returnClazz; } }Catch(Exception e) {E.printstacktrace (); } }Throw NewClassCastException (ClassName +"Not found exception"); }}
Note the point:
-
1.install method
The install method is mainly to copy the assets apk into a private directory, then traverse the private directory, use Bundledexclassloader to load the apk file, and then save these bundledexclassloader in the array.
-
2.loadClass method
The method first looks for the required class from the current ClassLoader, and if not found, traverses the lookup from the list.
Demo Run
In Mainactivity, we can call methods in the APK class in the following ways:
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 (),Ten, -,"Calculation Results"); 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 by Bundleclassloadermanager instead of the current ClassLoader.
Improvement Programme
As the LoadClass method in Bundleclassloadermanager, in fact we create a ClassLoader object, by overriding the current ClassLoader Findclass method, Then in the Findclass method of override, the class is first looked up from the current ClassLoader and then traversed from the bundledexclassloader, so that the classes in the bundle can be called in the host project. You can also call a class in host in the bundle.
new ClassLoader(super.getClassLoader()) { @Override protectedfindClass(String className) throws ClassNotFoundException { Class clazz = BundleClassLoaderManager.loadClass(context,className); ifnull) { thrownew ClassNotFoundException(className); } return clazz; } };
Summarize
The previous blog and this blog post will be loaded with classes. If the class you need to load is a tool class, there is no need to load resources, and so on, the above scenario is fine. But what if the loaded class is a UI such as fragment or activity, and you need to reference the resource file?
Next blog post: Offline loading of Android resources.
Reference
1.BaseDexClassLoader Source Code
Android Plugin (ii): Use Dexclassloader to dynamically load apk in assets