精华内容
下载资源
问答
  • 热更新
    千次阅读
    2022-03-28 21:36:41

    Android热更新

    组件化

    组件化和模块化其实一回事,都是拆分多个 module 进行开发,组件化的叫法更偏向封装系统功能,比如统一对话框封装,网络封装等,而模块化叫法更偏向业务方面,比如登录模块等。


    插件化

    App 的部分功能模块在打包的时候不以传统的方式打包进 apk 中,而是另一种方式封装到apk 内部,或者放在网络上适时下载,在需要的时候动态对这些功能模块进行加载,称之为插件化。

    这些单独二次封装的功能模块 apk,称为插件,初始安装的 apk 称为宿主。

    插件化基础是反射。


    反射

    Java 提供了关键字 public、private 用来限制代码之间的可见性,但又提供了反射来访问不可见方法。

    可见特性的支持并不是为了保护代码不被别人使用,而是为了程序开发的简洁性。可见性的的支持提供的是 Safety 的安全,而不是 Security 的安全。可见性的支持是让程序的开发者不容易写出 bug ,而不是更容易外部被入侵。

    反射的支持可以让开发者在可见性的不对外的时候,突破可见性的限制来调用自己需要的 API.


    反射的使用:

    package com.reflect.utils;
    class Utils {
      
      	private Utils(){}
      
        private int method(int a) {
            System.out.println("Utils.method打印了... a:" + a);
            return 10;
        }
    }
    
    
    
    // 通过反射调用 Utils 中的方法
    try {
        Class utilsClass = Class.forName("com.reflect.utils.Utils");// Utils.class
        Constructor constructor = utilsClass.getDeclaredConstructor();
        constructor.setAccessible(true);// 构造方法设置为可以访问
        Object utilsObj = constructor.newInstance();
        Method method = utilsClass.getDeclaredMethod("method", int.class);
        method.setAccessible(true);// 方法设置为可访问
        Object result = method.invoke(utilsObj, 1);
        System.out.println(result);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    

    DEX

    class:java 编译后的文件,每一个类对应一个 class 文件。

    dex:Dalvik Executable 把 class 打包到一起,一个 dex 可以包含多个 class 文件。

    odex:Optimized DEX 针对系统的优化,例如某个方法的调用指令,会把虚拟机的调用转换为使用具体的 index,这样在执行的就不用再查找了。

    oat:Optimized Android file Type,使用 AOT(Ahead-Of-Time compilation预先编译) 策略对 dex 预先编译成本地指令,这样在运行阶段就不需要再经历一次解释过程。


    插件化原理

    动态加载,通过自定义 ClassLoader 来加载新的 dex 文件,从而让程序员原本没有的类可以被使用。

    private void pluginLoad() {
        File pluginApk = new File(getFilesDir() + "/plugin.apk");
        AssetManager assets = getAssets();
        try(Source source = Okio.source(assets.open("plugin.apk"));
            BufferedSink sink = Okio.buffer(Okio.sink(pluginApk))) {
            sink.writeAll(source);
        } catch (IOException e) {
            e.printStackTrace();
        }
    
        // 调用 plugin.apk 中的方法
        DexClassLoader classLoader = new DexClassLoader(pluginApk.getAbsolutePath(),
                getCodeCacheDir().getPath(),
                null,
                null);
        try {
            Class<?> utilClass = classLoader.loadClass("com.chen.hotfix.utils.Utils");
            Constructor<?> constructor = utilClass.getDeclaredConstructors()[0];
            constructor.setAccessible(true);
            Object utilObj = constructor.newInstance();
            Method getNameMethod = utilClass.getDeclaredMethod("getName");
            getNameMethod.setAccessible(true);
            Object name = getNameMethod.invoke(utilObj);
            Log.e("TAG", "name:" + name);
    
        } catch (ClassNotFoundException
                | InvocationTargetException
                | IllegalAccessException
                | InstantiationException
                | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
    

    插件化和热更新
    1. 插件化的内容在原 App 中没有,而热更新是原 App 中的内容做了改动
    2. 插件化在代码中有固定的入口,而热更新则可能改变了任何有一个位置的代码

    热更新的原理

    ClassLoader 的 dex 文件的替换,直接修改字节码


    类加载过程

    双亲委托机制,是一个带缓存的,从上到下(从父到子)的加载过程。

    //ClassLoader.java
    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        // 首先判断该类型是否已经被加载
        Class c = findLoadedClass(name);
        if (c == null) {
            //如果没有被加载,就委托给父类加载或者委派给启动类加载器加载
            try {
                if (parent != null) {
                    //如果存在父类加载器,就委派给父类加载器加载
                    c = parent.loadClass(name, false);
                } else {
                    //如果不存在父类加载器,就检查是否是由启动类加载器加载的类,通过调用本地方法native Class findBootstrapClass(String name)
                    c = findBootstrapClass0(name);
                }
            } catch (ClassNotFoundException e) {
                // 如果父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
    

    对于一个具体的 ClassLoader,先从自己的缓存中取,自己缓存中没有,就找父 ClassLoader 要(parent.loadClass()),父 View 也没有,就自己加载。(父View的加载流程和自己是一样的)

    BaseDexClassLoader 的 findClass() 是通过它的 pathList.findClass(),它的 pathList.loadClass() 通过 DexPathList 的 dexElements 的 findClass(),所以热更新的关键在于,把补丁 dex 文件加载放进一个 Element,并且插入到 dexElements 这个数组的前面。


    手写热更新

    因为无法在更新之前就指定要更新谁,所以不能定义新的 ClassLoader,而只能选择对 ClassLoader 进行修改,让它能够加载补丁里面的类。

    因为补丁中的类在之前的应用中已经存在,所以应该把补丁中的 Element 对象插入到 dexElements 的前面才行,插入到后面会被忽略掉。


    具体的做法:

    1. 用补丁创建一个 PathClassLoader
    2. 把补丁 PathClassLoader 里面的 elements 插入到旧的 dexElements 前面去

    尽早加载热更新,通常是把加载过程放在 Application.attachBaseContext()
    热更新下载完之后需要先杀死程序才能让补丁生效
    用 d8 把指定的 class 打包进 dex


    sample

    HotfixApplication

    public class HotfixApplication extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
        }
    
        @Override
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(base);
            // 通过反射将dex的dexElements插入到应用的dexElements前面,使热更新(hotfix.dex)生效
            File hotfixDex = new File(getFilesDir() + "/hotfix.dex");
            if (hotfixDex.exists()) {
                try {
                    // originalLoader.pathList.dexElements = classLoader.pathList.dexElements;
                    // originalLoader.pathList.dexElements += classLoader.pathList.dexElements;
                    ClassLoader originalClassLoader = getClassLoader();// App的ClassLoader
                    DexClassLoader newClassLoader = new DexClassLoader(hotfixDex.getPath(),
                            getCodeCacheDir().getPath(), null, null);// dex的ClassLoader
    
                    Class<BaseDexClassLoader> loaderClass = BaseDexClassLoader.class;
                    Field pathListField = loaderClass.getDeclaredField("pathList");
                    pathListField.setAccessible(true);
                    Object pathListObj = pathListField.get(newClassLoader);
    
                    Class<?> pathListClass = pathListObj.getClass();
                    Field dexElementsField = pathListClass.getDeclaredField("dexElements");
                    dexElementsField.setAccessible(true);
                    Object dexElementsObj = dexElementsField.get(pathListObj);
    
                    Object originalPathListObj = pathListField.get(originalClassLoader);
                    Object originalDexElementsObj = dexElementsField.get(originalPathListObj);
    
                    int originalLength = Array.getLength(originalDexElementsObj);
                    int newLength = Array.getLength(dexElementsObj);
                    Object concatDexElementsObject = Array.newInstance(dexElementsObj.getClass().getComponentType(), originalLength + newLength);
                    for (int i = 0; i < newLength; i++) {
                        Array.set(concatDexElementsObject, i, Array.get(dexElementsObj, i));
                    }
                    for (int i = 0; i < originalLength; i++) {
                        Array.set(concatDexElementsObject, newLength + i, Array.get(originalDexElementsObj, i));
                    }
                    dexElementsField.set(originalPathListObj, concatDexElementsObject);
                } catch (NoSuchFieldException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    

    下载热更新包

    private void hotfixLoad() {
        File hotfixDex = new File(getFilesDir() + "/hotfix.dex");
        AssetManager assets = getAssets();
        try (Source source = Okio.source(assets.open("hotfix.dex"));//TODO 需要从网络下载hotfix.dex
             BufferedSink sink = Okio.buffer(Okio.sink(hotfixDex))) {
             sink.writeAll(source);
        } catch (IOException e) {
            e.printStackTrace();
        }
        android.os.Process.killProcess(android.os.Process.myPid());// 重启生效
    }
    

    更多相关内容
  • 逍遥西游热更新工具_逍遥热更新_逍遥西游_源码.zip,逍遥西游热更新工具,小九楼论坛.url,西游一键热更新.exe,SkinH_EL.dll
  • 今天小编就为大家分享一篇解决vuecli3.0热更新失效的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 今天小编就为大家分享一篇浅谈VUE-CLI脚手架热更新太慢的原因和解决方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • Auto.js的脚本热更新demo 热更新服务器已关闭,但代码还可以看看 使用说明请查看:使用说明.txt 备注:这个demo只是解决测试时反复打包的困扰, 不建议最终打包给用户时使用。 当然,如果你的用户不会抓包等操作, ...
  • U3D热更新框架

    2019-03-02 10:40:53
    实现自动打包资源,使用MD5码校验一键化CDN更新资源筛选,游戏启动热更新解决方案。脚本使用xlua。使用说明文档及框架源码下载
  • Autojs通过坚果云热更新源码
  • IntelliJ IDEA修改了mybatis mapper xml后自动热发布 热更新 热部署.zip Mybatis xml文件使用热加载检测不更新怎么办?看我的
  • Toou-OSGi:基于Qt跨平台技术打造的可热插拔,热更新框架!支持Widget,Quick
  • 白日门传奇手游提取客户端热更新文件
  • unity引擎下的,游戏底层资源加载框架+基于Jenkins的自动化打包+资源及代码热更新,的学习资料。
  • 主要介绍了解读golang plugin热更新尝试,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • es6 + webpack热更新

    2017-01-07 19:58:08
    主要是 用于 webpack + es6 + react 实现webpack 的热更新服务,webpack转es6,并没有react 其他代码,
  • electron-vue + vue-cli + element-ui 实时热更新版,electronv12.0.4框架,打包好的框架,拿来即用,实时热更新,提高开发3倍效率
  • 主要介绍了详解webpack 热更新优化,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 这篇文章中,详细的讲解了在Unity3d中如何进行IOS的热更新,想了解的同学可以看下!
  • 在上篇文章Flutter源码系列之Flutter的热更新探索(上)我们分析了Flutter的加载流程,找到了实现热更新的方法,接下来我们开始实现热更新功能。考虑到Google可能会在后续版本中对FlutterLoader类做修改,因此我们先...
  • cordova热更新.txt

    2020-08-17 11:58:19
    cordova热更新.txt
  • ToLua热更新原理.zip

    2020-06-23 11:10:01
    ToLua热更新原理,基于LuaFramework_UGUI的实现,便于学习游戏的热更新方法 https://github.com/getker/LuaFramework_UGUI
  • 实现tomcat热更新class文件
  • 主要介绍了golang使用信号量热更新的实现示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • js代码-webpack热更新

    2021-07-15 02:11:41
    js代码-webpack热更新
  • js代码-Node模块热更新

    2021-07-15 01:45:24
    js代码-Node模块热更新
  • 【教程】阿里云sophix移动App热更新.docx
  • 热更新app 不用更新整个APP.zip
  • android热更新方案,apaapatch
  • Utility components for react js
  • 热更新xlua.txt

    2019-09-27 14:17:20
    xluaunity2017热更新,视频 案例 资源齐全 仅供学习使用
  • react_native详细实现热部署、增量差异化热更新.pdf

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 202,888
精华内容 81,155
关键字:

热更新

友情链接: FX Eagle System.rar