Android to third-party class library runtime load

Source: Internet
Author: User

First, put the class libraries that need to be loaded at runtime into other directories of the project, such as creating a new Thirdlibs directory. Then use the DX command to compress the following class library into a Dex file.

DX--dex--output=. /assets/*.jar

This generates a Classes.dex file under assets and compresses it into a Classes.zip file.

Create a new class to load the class library


Import Java.io.file;import java.io.fileoutputstream;import java.io.filenamefilter;import java.io.IOException; Import Java.io.inputstream;import Java.io.outputstream;import Java.lang.reflect.array;import Java.lang.reflect.field;import Java.lang.reflect.invocationtargetexception;import Java.lang.reflect.Method; Import Java.util.arraylist;import java.util.arrays;import Java.util.hashset;import Java.util.list;import Java.util.listiterator;import Java.util.set;import Java.util.zip.zipfile;import Android.content.Context;import Android.content.pm.applicationinfo;import Android.content.pm.packagemanager;import Android.content.pm.packagemanager.namenotfoundexception;import Android.content.res.assetmanager;import Android.os.build;import Android.util.log;import Dalvik.system.dexfile;public class Assetsdex {static final String TAG = "Assetsdex";p rivate static final int max_supported_sdk_version = 20;private static final int min_sdk_version = 4;private s Tatic final set<string> installedapk = NEW hashset<string> ();p rivate static Boolean installed = False;private Assetsdex () {}public static void install (Conte XT context) {if (installed) {return;} Ensurelibs (context); LOG.I (TAG, "Install"), if (Build.VERSION.SDK_INT < min_sdk_version) {throw new RuntimeException ("Multi Dex Installation failed. SDK "+ build.version.sdk_int+" is unsupported. Min SDK version is "+ min_sdk_version+". "); try {applicationinfo ApplicationInfo = getapplicationinfo (context); if (applicationinfo = = null) {//Looks like running on A test Context, so just return without//Patching.return;} Synchronized (installedapk) {String Apkpath = applicationinfo.sourcedir;if (Installedapk.contains (Apkpath)) {return;} Installedapk.add (Apkpath), if (Build.VERSION.SDK_INT > Max_supported_sdk_version) {log.w (TAG, "Multidex is not Guaranteed to work in SDK version "+ build.version.sdk_int+": SDK version higher than "+ max_supported_sdk_version+" sho Uld be backed by "+" runtime with built-in Multidex capabilty but IT ' s not the "+" Case here:java.vm.version=\ "" + System.getproperty ("java.vm.version") + "\" ");} /* * The patched class loader is expected to be a descendant of * Dalvik.system.BaseDexClassLoader. We Modify its * dalvik.system.DexPathList pathList field to append additional * DEX file entries. */classloader loader;try {loader = Context.getclassloader ();} catch (RuntimeException e) {/* * Ignore those exceptions so That we don't break tests * relying in Context like a android.test.mock.MockContext * or a Android.content.ContextWrapper With a null base * Context. */LOG.W (TAG, "Failure while trying to obtain Context class loader. "+" must is running in test mode. Skip patching. ", e); return;} if (loader = = null) {//Note, the context class loader is null when running//robolectric tests. LOG.E (TAG, "Context class loader is null.") Must is running in test mode. "+" Skip patching. "); return;} File Dexdir = Context.getdir ("Outdex", context.mode_private); file[] Szfiles = dexdir.listfiles (New FilenameFilter() {@Overridepublic Boolean accept (File dir, String filename) {return Filename.endswith (". zip");}); list<file> files = new arraylist<file> (); for (File f:szfiles) {log.i (TAG, "load File:" + f.getname ()); files. Add (f);} LOG.I (TAG, "loader before:" + Context.getclassloader ()); installsecondarydexes (loader, dexdir, files); LOG.I (TAG, "loader end:" + Context.getclassloader ());}} catch (Exception e) {log.e (TAG, "Multidex installation Failure", e); throw new RuntimeException ("Multi Dex installation FAI LED ("+ e.getmessage () +"). "); installed = true; LOG.I (TAG, "Install Done");} private static ApplicationInfo Getapplicationinfo (context context) throws Namenotfoundexception {Packagemanager pm; String packagename;try {pm = Context.getpackagemanager ();p ackagename = Context.getpackagename ();} catch ( RuntimeException e) {/* * Ignore Those exceptions so, we don ' t break tests relying on * Context like a android.test.mo Ck. Mockcontext or A * Android.content.ContextWrapper with a null base ConText. */LOG.W (TAG, "Failure while trying-obtain applicationinfo from Context. "+" must is running in test mode. Skip patching. ", e); return null;} if (PM = = NULL | | packagename = = NULL) {//This is the most likely a mock-context, so just return without//Patching.return nu ll;} ApplicationInfo applicationinfo = Pm.getapplicationinfo (packagename,packagemanager.get_meta_data); return ApplicationInfo;} public static void Ensurelibs (context context) {Assetmanager Assetmanager = Context.getassets (); InputStream in = Null;out Putstream out = null;try {File Outdex = Context.getdir ("Outdex", context.mode_private); Outdex.mkdir (); File dex = Context.getdir ("Outdex", Context.mode_private);d Ex.mkdir (); in = Assetmanager.open ("Classes.zip"); File F = new file (dex, "Classes.zip"), if (F.exists () && f.length () = = In.available ()) {log.i (TAG, "Classes.zip No Change "); return;} LOG.I (TAG, "Classes.zip chaneged"); out = new FileOutputStream (f); byte[] buffer = new Byte[102400];int read;while (read = In.read (Buffer))! =-1) {out.write (buffer, 0, read);} In.close (); in = Null;out.flush (), Out.close (); out = null; LOG.I (TAG, "Classes.zip copy Over");} catch (Exception e) {e.printstacktrace ();}} private static void Installsecondarydexes (ClassLoader loader, File dexdir,list<file> files) throws Illegalargumentexception,illegalaccessexception, Nosuchfieldexception,invocationtargetexception, Nosuchmethodexception, IOException {if (!files.isempty ()) {if (Build.VERSION.SDK_INT >=) {v19.install (loader, files, dexdir);} else if (Build.VERSION.SDK_INT >=) {v14.install (loader, files, dexdir);} else {v4.install (loader, files);}}} /** * Locates a given field anywhere in the class inheritance hierarchy. * * @param instance * An object to search for the field into. * @param name * Field name * @return A Field object * @throws nosuchfieldexception * If the field C Annot be located */private static Field FindField (Object instance, String name) throws Nosuchfieldexception {foR (class<?> clazz = Instance.getclass (); Clazz! = null; clazz = Clazz.getsuperclass ()) {try {field field = Clazz.get Declaredfield (name), if (!field.isaccessible ()) {field.setaccessible (true);} return field;}  catch (Nosuchfieldexception e) {//Ignore and search Next}}throw new Nosuchfieldexception ("Field" + name + "not found in "+ Instance.getclass ());} /** * Locates a given method anywhere in the class inheritance hierarchy. * * @param instance * An object to search for the method into. * @param Name * Method Name * @param parametertypes * method Parameter Types * @return a method Obje  CT * @throws Nosuchmethodexception * If the method cannot be located */private static method FindMethod (Object instance, String Name,class<?> parametertypes) throws Nosuchmethodexception {for (class<?> clazz = Instan Ce.getclass (); Clazz! = NULL; Clazz = Clazz.getsuperclass ()) {try {method = Clazz.getdeclaredmethod (name, parametertypes); (!method.isaccessible ()) {method.setaccessible (true);} return method;} catch (Nosuchmethodexception e) {//Ignore and search Next}}throw new Nosuchmethodexception ("Method" + name + "with Para Meters "+ arrays.aslist (parametertypes) +" not found in "+ Instance.getclass ());} /** * Replace The value of a field containing a non null array, by a new array * containing the elements of the original A Rray plus the elements of * extraelements. * * @param instance * The instance whose field is to be modified. * @param fieldName * The field to modify. * @param extraelements * elements to append at the end of the array. */private static void Expandfieldarray (Object instance, String fieldname,object[] extraelements) throws Nosuchfieldexception,illegalargumentexception, illegalaccessexception {Field Jlrfield = FindField (instance, FieldName) object[] original = (object[]) Jlrfield.get (instance); object[] combined = (object[]) array.newinstance ( Original.getclass (). GetComponentType (), Original.length + extraelements.length); System.arraycopy (original, 0, combined, 0, original.length); System.arraycopy (extraelements, 0, combined, original.length,extraelements.length); LOG.I (TAG, "Install 4"); Jlrfield.set (instance, combined); LOG.I (TAG, "Install 5");} /** * Installer for platform versions 19. */private static Final class V19 {private static void install (ClassLoader loader,list<file> Additionalclasspathentries, File optimizeddirectory) throws IllegalArgumentException, Illegalaccessexception, Nosuchfieldexception, invocationtargetexception,nosuchmethodexception {/* * The patched class loader is expected to be a D Escendant of * Dalvik.system.BaseDexClassLoader. We Modify its * dalvik.system.DexPathList pathList field to append additional DEX * file entries. */LOG.I (TAG, "Install 1"); Field Pathlistfield = FindField (loader, "pathList"); Object dexpathlist = pathlistfield.get (loader); LOG.I (TAG, "Install 2"); Arraylist<ioexception> suppressedexceptions =New Arraylist<ioexception> (); Expandfieldarray (Dexpathlist, "dexelements", Makedexelements (DexPathList, new Arraylist<file> (additionalclasspathentries), optimizeddirectory,suppressedexceptions)); LOG.I (Tag, "Install 3"), if (suppressedexceptions.size () > 0) {for (IOException e:suppressedexceptions) {LOG.W (tag, "E Xception in Makedexelement ", e);} Field Suppressedexceptionsfield = FindField (loader, "dexelementssuppressedexceptions"); ioexception[] Dexelementssuppressedexceptions = (ioexception[]) suppressedexceptionsfield.get (loader); Dexelementssuppressedexceptions = = null) {dexelementssuppressedexceptions = Suppressedexceptions.toarray (new Ioexception[suppressedexceptions.size ()]);} else {ioexception[] combined = new Ioexception[suppressedexceptions.size () + dexelementssuppressedexceptions.length]; Suppressedexceptions.toarray (combined); System.arraycopy (Dexelementssuppressedexceptions, 0,combined, Suppressedexceptions.size (), Dexelementssuppressedexceptions.length);d exelementSsuppressedexceptions = combined;} Suppressedexceptionsfield.set (loader,dexelementssuppressedexceptions);}} /** * A Wrapper around * {@code private static final dalvik.system.dexpathlist#makedexelements} *. */private Static object[] makedexelements (Object dexpathlist,arraylist<file> files, File optimizeddirectory, Arraylist<ioexception> suppressedexceptions) throws Illegalaccessexception, InvocationTargetException, nosuchmethodexception {log.i (TAG, "Install 9"); Method makedexelements = FindMethod (dexpathlist, "makedexelements", Arraylist.class, File.class, ArrayList.class); Return (object[]) Makedexelements.invoke (dexpathlist, Files,optimizeddirectory, suppressedexceptions);}} /** * Installer for platform versions, 18. */private static Final class V14 {private static void install (ClassLoader loader,list<file> Additionalclasspathentries, File optimizeddirectory) throws IllegalArgumentException, Illegalaccessexception, Nosuchfieldexception, Invocationtargetexception,nosuchmethodexception {/* * The patched class loader is expected to be a descendant of * Dalvik.system.BaseDexClassL Oader. We Modify its * dalvik.system.DexPathList pathList field to append additional DEX * file entries. */field Pathlistfield = FindField (loader, "pathList"); Object dexpathlist = pathlistfield.get (loader); Expandfieldarray (Dexpathlist, "dexelements", Makedexelements (Dexpathlist, New arraylist<file> (Additionalclasspathentries), optimizeddirectory));} /** * A Wrapper around * {@code private static final dalvik.system.dexpathlist#makedexelements} *. */private Static object[] makedexelements (Object dexpathlist,arraylist<file> files, File optimizeddirectory) Throws Illegalaccessexception, invocationtargetexception,nosuchmethodexception {Method makedexelements = FindMethod ( Dexpathlist, "Makedexelements", Arraylist.class, File.class); return (object[]) Makedexelements.invoke (DexPathList, files,optimizeddirectory);}} /** * Installer for platform versions 4 to 13. */prIvate static Final class V4 {private static void install (ClassLoader loader,list<file> additionalclasspathentries) Throws IllegalArgumentException, Illegalaccessexception,nosuchfieldexception, IOException {/* * the patched class Loader is expected to be a descendant of * Dalvik.system.DexClassLoader. We modify its fields mpaths, * mfiles, Mzips and Mdexs to append additional DEX file entries. */int extrasize = Additionalclasspathentries.size (); Field Pathfield = FindField (loader, "path"); StringBuilder path = new StringBuilder ((String) pathfield.get (loader)); string[] extrapaths = new String[extrasize]; file[] Extrafiles = new File[extrasize]; zipfile[] extrazips = new Zipfile[extrasize];D exfile[] Extradexs = new Dexfile[extrasize];for (listiterator<file> iterator = Additionalclasspathentries.listiterator (); Iterator.hasnext ();) {File additionalentry = Iterator.next (); String Entrypath = Additionalentry.getabsolutepath ();p ath.append (': '). Append (entrypath); int index = Iterator.prevIousindex (); Extrapaths[index] = Entrypath;extrafiles[index] = Additionalentry;extrazips[index] = new ZipFile ( Additionalentry); Extradexs[index] = Dexfile.loaddex (Entrypath, entrypath+ ". Dex", 0);} Pathfield.set (loader, path.tostring ()), Expandfieldarray (loader, "mpaths", extrapaths); Expandfieldarray (Loader, " Mfiles ", extrafiles); Expandfieldarray (loader," Mzips ", extrazips); Expandfieldarray (loader," Mdexs ", Extradexs);}}}

In the program initialization, such as application or startup activity, with Assetsdex.install (context), load the class library, it must be loaded before using the class library. If the class library is too large, the first load may be slow. The advantage of this method is: 1, the breakthrough 64K limit 2, Eclipse running fast (need to pack the Lib reduced)

Android to third-party class library runtime load

Related Article

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.