其实热修复就这么简单

庖丁解牛--插件化和热修复

上几篇内容介绍了Java的ClassLoader和相关的知识点,总的来说 · Java加载class逻辑是双亲委托模式 · 对于不在class path中的class,可以通过自定义class loader来加载 · 同一个class的不同对象是否可以转换还要看是否在同一个class loader里

明白了这几点之后,我们就可以自己来实现一个热修复框架了。

热修复原理

现在技术圈的热修复可以分为几种套路, · 生成差分包 patch.dex,启动时通过反射把dex放到classloader的Element[]前部 · 生成差分包 patch.dex,将patch和原dex合并,启动时加载合并后的新dex · 通过native进行运行时的方法级替换

以上三种套路,代表框架可以参考 · Nuwa,RocooFix,手Q团队提出 · Tinker,微信团队提出 · AndFix,阿里百川

这几种都是成熟的框架,可以直接使用。其实在明白了ClassLoader的原理后,我们自己也可以造自己的轮子,这样对热修复的原理能有个深刻的认识。

组件化开发热修复

"插件化开发"已经是老生常谈了,但其实说起来很简单,简单的举例,把项目中不同的功能通过接口来隔离,就算是组件化开发了。

举个非常简单的例子,对于不同功能或者渠道我们需要展示不同的Toast内容,这样可以把内容获取做成接口暴露给app,运行时动态的加载不同的class来显示不同的toast 最终完成的代码接口如下图

Alt text

首先定义接口,实现插件

我们先定义一个接口给app和组件/插件,或者说 component比较没有歧义

package com.phoenix.hotswitch;
public interface CustomInterface {
    public String getText();
}

这个接口一式两份,在主工程和插件工程里都有,然后我们在插件中需要实现实现这个接口

package com.phoenix.hotswitch;
public class CustomImpl implements CustomInterface {
    private static final String EXT_STRING = "hello from the other side";
    @Override
    public String getText() {
        return EXT_STRING;
    }
}

这样就完成了插件的代码了,我们要把它打成jar包,一般jar包里的是class文件,Android的dalvik/ant是不能直接加载的,我们需要用sdk下的platform-tools的dex工具再把jar包打一次。

主工程实现插件的调用

主工程需要有一个类,这个类用自定义的ClassLoader来加载插件,然后通过反射获取插件的实现类,通过上一步我们定义好的接口来调用实现类。 姑且把这个类叫 ToastFactory,它有几个接口,分别是

public class ToastFactory {
    public void showCustomToast(Type type){...}
    private String getMsgFromInernal(){...}
    private String getMsgFromExt(){...}
    public void loadClass() {...}
}

loadClass()做了这么件事情, 加载外部class,然后实例化给mExt,调用的时候就可以通过之前定义好的接口来使用了。

public void loadClass() {
    String target = extractJarFromAsset();
    if(TextUtils.isEmpty(target)) {
        return;
    }

    DexClassLoader loader = new DexClassLoader(target, mContext.getExternalCacheDir().getAbsolutePath(), null, mContext.getClassLoader());

    try {
        Class extImpl = loader.loadClass("com.phoenix.hotswitch.CustomImpl");
        mExt = (CustomInterface) extImpl.newInstance();
        Toast.makeText(mContext, "ext class loaded success", Toast.LENGTH_SHORT).show();
    } catch (Exception exp) {
        exp.printStackTrace();
    }
}

在没有 loadClass 之前,点 external toast会展示默认的toast,点了 loadClass 之后会把插件加载进来,再展示外部Toast的时候,就会展示组件里的Toast了。

这里为什么要用接口的原因是为了让模块之间去耦合,避免模块间耦合度高,而在编程的过程中不小心导致的一些问题,比如ClassCastException。

然后我们就可以在主工程中任意一个地方通过ToastFactory来使用组件的功能了。完成的效果如下面的gif图所示。

应用

这种app架构有个好处,就是各个功能之间绝对独立,在开发的时候可以各个小组分别开发,最终以jar包的形式给主工程通过约定好的接口使用。 而这种方式还可以实现在线的功能更新,只需要下发不同的jar包,在统一的接口约束下线上的app可以实现不同的功能。 我把这个demo的代码上传到了GitHub上,有兴趣的可以下载下来研究源码。

原文发布于微信公众号 - Android每日一讲(gh_f053f29083b9)

原文发表时间:2018-04-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏宋凯伦的技术小栈

Android - 通过真实案例学习解内存泄漏问题,最终发现Android原生Bug

  作为一个Android新手小白,刚到新公司,最近的工作就是在学习解各类Bug。转型之初,面临各种新知识,会有压力,但是学习的过程是快乐的。   上周刚遇上一...

23880
来自专栏FreeBuf

VirtualApp技术黑产利用研究报告

一、 前言 VirtualApp(以下称VA)是一个App虚拟化引擎(简称VA)。VirtualApp创建了一个虚拟空间,你可以在虚拟空间内任意的安装、启动和卸...

44080
来自专栏游戏开发那些事

【Unity游戏开发】AssetBundle杂记--AssetBundle的二三事

  马三在公司大部分时间做的都是游戏业务逻辑和编辑器工具等相关工作,因此对Unity AssetBundle这块的知识点并不是很熟悉,自己也是有打算想了解并熟悉...

42820
来自专栏程序员互动联盟

【专业技术第十讲】嵌入式系统的中断流程剖析

存在问题: 搞嵌入式特别是底层,常常提到中断,中断时干什么的呢? 解决方案: 做嵌入式肯定要了解中断。本文根据实例详细介绍中断过程,包括软件和硬件方面。 示例:...

47560
来自专栏FreeBuf

ADB配置提权漏洞(CVE-2017-13212)原理与利用分析

0x01 背景 adb由于拥有shell权限,因此仅在授权PC端后才可使用shell权限,而通过该漏洞,可以实现在移动端获取shell权限,以致于可随意删除应用...

46190
来自专栏腾讯Bugly的专栏

《Android插件化技术——原理篇》

| 导语 插件化技术最早从2012年诞生至今,已经走过了5个年头。从最初只支持Activity的动态加载发展到可以完全模拟app运行时的沙箱系统,各种开源项目层...

3K90
来自专栏文大师的新世界

9. redux如何精简代码

通过之前的代码不难看出redux系统里的ActionType、Action、Reducer都有一定的共性,小项目无所谓,这样写更清晰,但是一旦组件以及业务增多,...

19150
来自专栏Flutter入门到实战

Android M Launcher3主流程源码浅析

关于Launcher是啥的问题我想这里就没必要再强调了。由于一些原因迫使最近开始需要研究一下Launcher3源码,为了不再像以前那么傻逼(研究Settings...

22120
来自专栏移动开发

仿googleMVP设计模式示例demo,结合rxjava,retrofit2.0

这是模仿Google官方mvp示例的小demo,结合Retrofit2.0,rxjava加上自己的一点理解所写. 感谢干货免费开放的API.

14930
来自专栏前端小作坊

Web Animations的命名简化

最早支持Web Animation的浏览器是Chrome 36,在Chrome 39中又更新了对播放的控制。在Javascript中我们可以调用Element....

7410

扫码关注云+社区

领取腾讯云代金券