详解Android中实现热更新的原理
这篇文章就来介绍一下android中实现热更新的原理。
一、classloader
我们知道java在运行时加载对应的类是通过classloader来实现的,classloader本身是一个抽象来,android中使用pathclassloader类作为android的默认的类加载器,pathclassloader其实实现的就是简单的从文件系统中加载类文件。pathclassloade本身继承自basedexclassloader,basedexclassloader重写了findclass方法,该方法是classloader的核心。
@override protected class> findclass(string name) throws classnotfoundexception { list suppressedexceptions = new arraylist(); class c = pathlist.findclass(name, suppressedexceptions); if (c == null) { classnotfoundexception cnfe = new classnotfoundexception("didn't find class /"" + name + "/" on path: " + pathlist); for (throwable t : suppressedexceptions) { cnfe.addsuppressed(t); } throw cnfe; } return c; }
看源码可知,basedexclassloader将findclass方法委托给了pathlist对象的findclass方法,pathlist对象是在basedexclassloader的构造函数中new出来的,它的类型是dexpathlist。看下dexpathlist.findclass源码是如何做的:
public class findclass(string name, list suppressed) { for (element element : dexelements) { dexfile dex = element.dexfile; if (dex != null) { class clazz = dex.loadclassbinaryname(name, definingcontext, suppressed); if (clazz != null) { return clazz; } } } if (dexelementssuppressedexceptions != null) { suppressed.addall(arrays.aslist(dexelementssuppressedexceptions)); } return null; }
直接就是遍历dexelements列表,然后通过调用element.dexfile对象上的loadclassbinaryname方法来加载类,如果返回值不是null,就表示加载类成功,会将这个class对象返回。而dexelements对象是在dexpathlist类的构造函数中完成初始化的。
this.dexelements = makedexelements(splitdexpath(dexpath), optimizeddirectory, suppressedexceptions);
makedexelements所做的事情就是遍历我们传递来的dexpath,然后一次加载每个dex文件。
二、实现
上面分析了android中的类的加载的流程,可以看出来dexpathlist对象中的dexelements列表是类加载的一个核心,一个类如果能被成功加载,那么它的dex一定会出现在dexelements所对应的dex文件中,并且dexelements中出现的顺序也很重要,在dexelements前面出现的dex会被优先加载,一旦class被加载成功,就会立即返回,也就是说,我们的如果想做hotpatch,一定要保证我们的hotpacth dex文件出现在dexelements列表的前面。
要实现热更新,就需要我们在运行时去更改pathclassloader.pathlist.dexelements,由于这些属性都是private的,因此需要通过反射来修改。另外,构造我们自己的dex文件所对应的dexelements数组的时候,我们也可以采取一个比较取巧的方式,就是通过构造一个dexclassloader对象来加载我们的dex文件,并且调用一次dexclassloader.loadclass(dummyclassname);
方法,这样,dexclassloader.pathlist.dexelements中,就会包含我们的dex,通过把dexclassloader.pathlist.dexelements插入到系统默认的classloader.pathlist.dexelements列表前面,就可以让系统优先加载我们的dex中的类,从而可以实现热更新了。
下面展示一部分代码
private static synchronized boolean injectaboveequalapilevel14( string dexpath, string defaultdexoptpath, string nativelibpath, string dummyclassname) { log.i(tag, "--> injectaboveequalapilevel14"); pathclassloader pathclassloader = (pathclassloader) dexinjector.class.getclassloader(); dexclassloader dexclassloader = new dexclassloader(dexpath, defaultdexoptpath, nativelibpath, pathclassloader); try { dexclassloader.loadclass(dummyclassname); object dexelements = combinearray( getdexelements(getpathlist(pathclassloader)), getdexelements(getpathlist(dexclassloader))); object pathlist = getpathlist(pathclassloader); setfield(pathlist, pathlist.getclass(), "dexelements", dexelements); } catch (throwable e) { e.printstacktrace(); return false; } log.i(tag, "
android中实现热更新的原理先为大家介绍到这,大家可以结合平时积累的知识,查阅相关书籍进行深入学习探究,希望大家能够有所收获。