从”苹果与微信二选一“事件中学习”热更新“

一.事件背景

今年3月6日,苹果召开了全球开发者大会,正式推出了iOS 11操作系统。本来苹果开一个操作系统的发布会,如果没有随之推出新机器,其实影响力更多的还是限于开发界,对普通用户的影响还不至于有多大。

但是在这个发布会召开后,网上就有爆料,说让苹果与微信二选一了,而且还说的头头是道,把苹果”全面禁用热更新“作为证据来进行实锤。也就是说如果微信不禁用热更新功能,并且双方无法达成妥协的话,苹果或将微信下架!这一下子引发了很多人的关注,那么多的苹果手机用户该咋办,到底是不用果机了还是不用微信了?

当然后来腾讯公关总监张军在其认证微博转发相关微博时赶紧辟谣,表示没有二选一这事,让广大吃瓜群众不用瞎担心。

好了,吃瓜群众们,别瞎担心了,都没啥事,该吃瓜就去吃瓜吧,别操心了。

二.热更新概述

但是作为程序员的我们,可不应该只会跟着”吃瓜“,咱们是不是应该了解一下好几年前就出现的”热更新“这个技术呢?

所以鉴于有些朋友对”热更新“这个技术还不是很了解,那我就借此机会,简单给大家科普一下”热更新“哈。

三.更新的含义

首先这里说的”更新“,指的是我们的项目(web/app等)上线后,后续可能需要持续的解决存在的bug,推出新功能,那么无论是修复bug还是发布新功能,总之你都要更改原有的线上项目,这个过程也就是说为的更新!

四.热更新的含义

咱们以iOS App的更新为例,说一下热更新的特点。

在iOS系统中,App软件的更新方式通常有2种方案。一种是在AppStore内进行更新,也就是我们在更新App时重新下载全部安装包,然后把这个安装包重新安装一遍,并且覆盖掉原有的App。而另一种更新方式就是热更新,热更新会动态的下发代码,在用户打开App时会发现需要更新的软件包,此时在更新时只需下载安装更新部分的代码,不用下载全部的App代码,这样就可以实现不停机更新,实时更新。

所以这个热更新的优点显而易见,更新包小,更新速度快,操作简单,其实就是一种所谓的”打补丁“。那作为普通用户的大众,应该更喜欢热更新的解决方案,因为我们不用耗费那么多流量和时间去更新最新的功能对吧!在Android系统中,一般主流的App都是采用热更新的技术方案,但是为啥到了苹果他们家就不行了呢?

说到这,就不得不说苹果系统的一大特点了,什么特点呢?不开源呗!封闭的!而Android系统属于Linux的一个分支,是典型的开源软件,只要你符合开源协议,随你折腾去。

在热更新的这种技术方案里面,它会动态下发代码,这样开发者就可以在不发布新版本的情况下,修复 App存在的BUG ,也可以直接发布新功能,从而就可以绕开苹果的审核机制。那为啥要绕开苹果的审核机制呢?

没做过iOS开发的朋友可能不清楚,你是不知道在AppStore里面审核一个功能要花多长时间,可能写一段功能代码只需要1天,保不齐苹果给你审核都得两三天,一个项目开发周期里面,经常这样审核,时间浪不浪费?而且不是说你发起申请就一定能过的,有时候等了半天,还给你拒了,闹心的要死。就比如我以前开发的时候,我们项目组的那个iOS妹子要上线充值买课程的功能,在Android系统里面直接引入支付宝或微信就好了。但是苹果系统里不行,你是不能直接花钱买虚拟的课程,必须在自己的项目中设计一个“虚拟币”,用钱买这个虚拟币,在用虚拟币买课程资源,比Andriod系统麻烦的多!

五.热更新的安全隐患

那么此时又有朋友可能好奇了,苹果咋这么膈应人呢,整这么多弯弯绕干嘛,跟Android一样,别那么多事得了呗!

其实这还是因为苹果系统属于封闭系统,不开源,为了保证自身系统的安全性,它害怕你这个App软件热更新时绕过它的审核机制,此时万一有些黑客开发者通过提交正常的版本之后,再通过热更新的方式修改APP从而导致一些安全隐患。所以苹果干脆就一刀切,都必须按我说得来,不允许热更新,这样既能改善部分使用混编语言开发的App的流畅性,也能牢牢的掌握App的审核权限。反正我是老大,我说了算,你不听我的就不能在我这里上线App!

六.Android热更新的实现原理

Android的热更新实现的理论基础是Java中的ClassLoader类加载机制!
首先在Java程序中JVM虚拟机会通过类加载器ClassLoader,来加载class文件和jar文件(本质还是class文件)。而在Android系统中,本身原生的Android开发就是用的Java,所以Android中也有类加载器。但是Android的虚拟机不是传统的JVM虚拟机,而是Dalvik或ART(4.4之后出现)虚拟机,这种虚拟机里面加载的是dex文件,不再是jar文件,但本质上还是class文件,只不过是做了一定程度的优化。

在Android中类加载器分为两种类型,分别是系统ClassLoader和自定义ClassLoader,其中系统ClassLoader中又包括三种,分别是BootClassLoader、PathClassLoader和DexClassLoader加载器。

DexClassLoader一般用来加载自定义的dex文件以及包含dex的apk文件或jar文件,也支持从SD卡进行加载。

而PathClassLoader是安卓中默认的类加载器,它一般可以加载系统类和应用程序的类,如果是加载非系统应用程序类,则会加载data/app/目录下的dex文件以及包含dex的apk文件或jar文件。当PathClassLoader加载类时可以通过findClass()方法找到所需要的class字节码。而这个findClass()方法最终是通过遍历DexPathList中的Element[]数组,进而加载我们需要的类。那么要想实现热更新只需要在出现问题的类还没加载前,把作为补丁那个Element代码段插入到数组前面,这样加载的时候就会优先加载已经修复的类,从而实现了bug的修复和功能的更新!

七.Android热更新的实现思路

接下来我们就可以来简单说一下实现思路。

1.通过DexClassLoader加载补丁代码,然后利用Java的反射技术拿到生成的Element[]数组;

2.获取安卓中默认的类加载器PathClassLoader,然后也是利用反射技术拿到Element[]数组;

3.将补丁Element[]和系统的Element[]数组合并(补丁元素放在合并数组前面),并重新赋值给PathClassLoader进行加载。

八.iOS热更新的实现思路

在iOS系统里,要想实现热更新,可以采用一些第三方的解决方案,比如JsPatch,lua脚本,Weex,React Native,动态库,Hybrid等。

简单说一下JsPatch实现的原理:

JsPatch利用Objective-C 是动态语言的特点,OC上所有方法的调用/类的生成都通过 Objective-C Runtime 在运行时进行,与Java中类型,可以通过类名和方法名反射得到相应的类和方法,也可以替换某个类的方法为新的实现,还可以新注册一个类,从而为类添加方法,实现通过JS调用,然后改写OC方法。

思路就是利用JS传递字符串给OC,OC通过 Runtime 接口调用和替换OC方法。

九.Java Web热更新的实现思路

在持续交付的时代,重新部署一个新的版本只需要点击一下按钮。但在有的情况下,重新部署过程可能比较复杂,停机是不被允许的。所以JVM提供了另外一种选择:在不重启应用的前提下进行小幅改动,又称热更新。

对于很多的大型的网站或者应用来说,如果每次更新项目,都进行一次重新,那可能就要花费大量的时间成本。所以,这种大型的商业应用,一般都是不允许停机,起码不会全部停机的,但是项目又得修复已有的bug,添加最新的功能,那么在Java Web中,热更新技术也是迫切需要的。在目前的Java虚拟机中,还只能实现方法级别的热更新,当对整个类的结构进行了修改后,仍然需要重启虚拟机。

Java 热更新的演变探索

JDK 1.4的时候,开始JPDA引入了hotSwap机制(JPDA Enhancements),实现了debug时的method body的动态性。

JDK 1.5的时候,开始通过JVMTI实现的java.lang.instrument(Java Platform SE 8)的premain方式,实现了agent方式的动态性(JVM启动时指定agent)。

JDK 1.6的时候,增加了agentmain方式,实现了运行时动态性(通过The Attach API 绑定到具体VM)。其基本实现是通过JVMTI的retransformClass/redefineClass进行函数体级别的字节码更新,ASM、CGLib之类基本都是围绕这些在做动态性。

Arthas方案介绍

Arthas是阿里巴巴最近开源出来的一个针对java的工具,主要是针对java的问题进行诊断,它协助完成下面这些功能:

  1. 这个类是从哪个jar包加载而来的?
  2. 为什么会报各种类相关的Exception?
  3. 线上遇到问题无法debug好蛋疼,难道只能反复通过增加System.out或通过加日志再重新发布吗?
  4. 线上的代码为什么没有执行到这里?是由于代码没有commit?还是搞错了分支?
  5. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现怎么办?
  6. 是否有一个全局视角来查看系统的运行状况?
  7. 有什么办法可以监控到JVM的实时运行状态?

arthas实现热更新

使用Arthas三个命令就可以搞定热更新

jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java

mc /tmp/UserController.java -d /tmp

redefine /tmp/com/example/demo/arthas/user/UserController.class
  1. jad命令反编译,然后可以用其它编译器,比如vim来修改源码;
  2. mc命令来内存编译修改过的代码;
  3. 用redefine命令加载新的字节码。
发布了246 篇原创文章 · 获赞 79 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/syc000666/article/details/105083330
今日推荐