欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解Android中实现热更新的原理

程序员文章站 2024-02-13 10:48:16
这篇文章就来介绍一下android中实现热更新的原理。 一、classloader 我们知道java在运行时加载对应的类是通过classloader来实现的,class...

这篇文章就来介绍一下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中实现热更新的原理先为大家介绍到这,大家可以结合平时积累的知识,查阅相关书籍进行深入学习探究,希望大家能够有所收获。