美文网首页
ClassLoader和热更新

ClassLoader和热更新

作者: NullBugs | 来源:发表于2018-09-30 10:14 被阅读0次

标签(空格分隔): classloader multidex 热更新


前言:Android P的发布,使得一大批使用第三方热更新框架的APP一片哀嚎(核心原因是:Android P 禁止应用通过反射,JNI等方式调用系统的非SDK方法,第三框架或多或少都用到了反射,在Android P上APP的功能失效)
感谢磊神分享

Java Classloader

  • 加载对象:字节码文件
  • 动态加载:运行时需要该类时再加载到JVM
  • 父加载器与委托加载
  • 常见的类加载器:
    • BootstrapClassloader
      纯c++实现的类加载器,没有对应的java类,主要加载的jre/lib/目录下的核心库
    • ExtClassloader
      主要加载jre/lib/ext/目录下的扩展包
    • AppClassloader
      主要加载java classpath路径下的包
20170210192931505.png

类的继承关系:

注:
ExtClassLoader.java 和 AppClassLoader.java本身不具有类的继承关系,他们共同继承URLClassLoader.java

    /*
     * Returns the class loader used to launch the main application.
     */
    public ClassLoader getClassLoader() {
        return loader;
    }
    /*
     * The class loader used for loading installed extensions.
     */
    static class ExtClassLoader extends URLClassLoader {}

/**
     * The class loader used for loading from java.class.path.
     * runs in a restricted security context.
     */
    static class AppClassLoader extends URLClassLoader {}

Parent通过ExtClassLoader 构建 APPClassLoader:

        ClassLoader extcl;
        try {
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {
            throw new InternalError(
                "Could not create extension class loader", e);
        }

        // Now create the class loader to use to launch the application
        try {
        //将ExtClassLoader对象实例传递进去
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {
            throw new InternalError(
                "Could not create application class loader", e);
        }
1071527397.jpg

ClassLoader 测试代码

        ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println("app ClassLoader : " + appClassLoader);
        System.out.println("app ClassLoader class : " + appClassLoader.getClass());
        System.out.println("app ClassLoader load Path : " + System.getProperty("java.class.path"));
        
        System.out.println("-------------------------");
    
        ClassLoader extClassLoader = appClassLoader.getParent();
        System.out.println("ext ClassLoader : " + extClassLoader);
        System.out.println("ext ClassLoader class : " + extClassLoader.getClass());
        System.out.println("ext ClassLoader load Path : " + System.getProperty("java.ext.dirs"));
        
        System.out.println("-------------------------");

        ClassLoader bootClassLoader = extClassLoader.getParent();
        System.out.println("boot ClassLoader : " + bootClassLoader);
        System.out.println("boot ClassLoader load Path : " +(System.getProperty("sun.boot.class.path")));

执行结果如下:

app ClassLoader : sun.misc.Launcher$AppClassLoader@c387f44
app ClassLoader class : class sun.misc.Launcher$AppClassLoader
app ClassLoader load Path : D:\Android_work\java\ClassLoaderTest\bin
-------------------------
ext ClassLoader : sun.misc.Launcher$ExtClassLoader@659e0bfd
ext ClassLoader class : class sun.misc.Launcher$ExtClassLoader
ext ClassLoader load Path : D:\soft\devsoft\soft\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
-------------------------
boot ClassLoader : null
boot ClassLoader load Path : D:\soft\devsoft\soft\jre\lib\resources.jar;D:\soft\devsoft\soft\jre\lib\rt.jar;D:\soft\devsoft\soft\jre\lib\sunrsasign.jar;D:\soft\devsoft\soft\jre\lib\jsse.jar;D:\soft\devsoft\soft\jre\lib\jce.jar;D:\soft\devsoft\soft\jre\lib\charsets.jar;D:\soft\devsoft\soft\jre\lib\jfr.jar;D:\soft\devsoft\soft\jre\classes

为什么要引入父委托加载机制

  • 保证软件系统的安全性,防止注入恶意代码
  • 可以通过父委托机制实现"伪继承"的方式调用系统私有方法或者实现多版本SDK兼容
    例如:
    String.java此方法不公开,而我们想使用,最常用的方式是反射。介绍另外一种方法利用ClassLoader的加载顺序实现"伪继承":
    1.在应用中构建一个同包名和同函数的String方法,并将非公开的方法设置为public,在编译时会使用应用中的String,运行时由于ClassLoader的父委托机制,优先加载系统类(public等关键字在编译时后无效)
 /**
     * Copy characters from this string into dst starting at dstBegin.
     * This method doesn't perform any range checking.
     */
    void getChars(char dst[], int dstBegin) {
        System.arraycopy(value, 0, dst, dstBegin, value.length);
    }

Android Classloader

  • 加载对象:dex文件、jar及apk
  • 常用类加载器:
    • BootClassLoader
      所有类加载器的parent,是ClassLoader的内部类;和java的BootStrapClassLoader不一样,是由java代码实现的
    • PathClassLoader(系统默认加载器)
      一般加载已安装好的apk文件,/data/app目录下
      • DVM虚拟机(JIT)
      • ART虚拟机(AOT) 可加载未安装的dex、jar或者apk文件
    • DexClassLoader
       加载路径需要在创建DexClassLoader时传入,所以可以加载任何路径下的dex、jar或者apk文件
192717809.jpg
  • 动态加载的两种方式(个人理解其实就是热更新实现的两种方式)
    • 将DexClassLoader放在PathClassLoader和BootClassLloader之间
    • 将DexClassLoader要加载的文件路径追加到PathClassLoader的DexPathList的后面
337439105.jpg 1854581320.jpg

Android Multidex(分包)

  • 分包机制
    • 什么是分包
304680740.jpg
- 解决65535问题
203851534.jpg
在android 5.0之前,DVM虚拟机用short类型的变量保存了一个dex文件中的方法数,所以最大值是65535
  • 分包方法
    • gradle本身支持自动分包,清单文件配置即可
      • 配置依赖"com.android.support:multidex:1.0.0"
      • 启动multidex: multiDexEnable true
      • 在application里配置: MultiDex.install()
    • 开发者自己分包,自己动态加载
      将class文件转为dex文件:
      使用android sdk里的build-tool下的dx工具,执行
      dx --dex --output=xxx.dex xxx.class
带包名的类转dex注意: 最后一个参数应当携带包的相对路径,例如:
dx –-dex –-output=xxx.dex 包名/xxx.class

基于类加载器实现的简单热更新Demo

准备操作:生成补丁文件与搭建后台

  • dx --dex --output=patch.dex com/sensetime/classloader/Statistic.class
  • java servlet搭建简单后台
  • 客户端启动检查是否要热更新,进行多线程下载补丁文件
  • 将下载好的补丁文件拷贝到应用私有目录下
  • 动态加载补丁文件

第一种实现

  • 将DexClassLoader放在PathClassLoader和BootClassLoader之间

第二种实现

  • 将DexClassLoader要加载的文件路径追加到PathClassLoader的DexPathList的后面

结语:第三方热更新的框架基本都是通过这两种方式实现热更新,但是在Android P上或多或少都存在问题,建议大家尽量使用Android 标准的接口,原因在于Android逐渐加强了系统安全控制,第三方框架或多或少有很多灰色地带。

参考

android mutidex的原理和实现

相关文章

  • ClassLoader和热更新

    标签(空格分隔): classloader multidex 热更新 前言:Android P的发布,使得一大批使...

  • ClassLoader和热修复

    Android源码来自28.0.2 ClassLoader 参考Android工程师进阶 34讲1.每个Class...

  • 2019阅读记录

    [toc] 2019阅读记录 热更新 ClassLoader理解 DexElements 由一个个dex文件组成的...

  • Android进阶解密⑤—热修复

    在此之前已经总结过ClassLoader的原理,以及通过ClassLoader方式实现的热修复思路,实现热修复的方...

  • Android热更新实现原理浅析

    热更新是Android工程师必学的技能之一,其理论基础就是ClassLoader类加载器。我们知道,在Java程序...

  • 热修复原理与基础范例

    原理 ClassLoader 与 双亲委托 热修复建立的基础是 ClassLoader 的加载机制。 Androi...

  • 了解ClassLoader

    了解甚至理解ClassLoader的作用以及工作机制,可以帮助我们更快的上手复杂的框架或者知识。比如热更新,插件化...

  • Android Classloader热修复

    惯例段子。 阅读本文你可以掌握,热修复的原理和简单实现. 目录Classloader热修复原理热修复代码实现面试知...

  • Android热修复【实战二】

    接着说说热修复,补充两个问题 反射中Class.forName()和ClassLoader.loadClass()...

  • 探秘 Java 热部署二(Java agent premain

    # 前言 在前文 探秘 Java 热部署 中,我们通过在死循环中重复加载 ClassLoader 和 Class ...

网友评论

      本文标题:ClassLoader和热更新

      本文链接:https://www.haomeiwen.com/subject/zagioftx.html