前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >其实热修复就这么简单

其实热修复就这么简单

作者头像
PhoenixZheng
发布2018-08-07 16:42:31
3420
发布2018-08-07 16:42:31
举报
庖丁解牛--插件化和热修复

上几篇内容介绍了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上,有兴趣的可以下载下来研究源码。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-04-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android每日一讲 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 庖丁解牛--插件化和热修复
  • 热修复原理
  • 组件化开发热修复
    • 首先定义接口,实现插件
      • 主工程实现插件的调用
      • 应用
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档