# 前言

上个周末学校跟蚂蚁金服合作进行了为期三天的科技创(培)新(训)营(班)活动,课程主题主要围绕着 “如何创造出支付宝移动 APP”,说到支付宝小程序,我就想到去年有幸参加了 D2 开发者大会,其中我觉得一半的时间是在推广支付宝小程序。鄙人拙见,小程序非常方便,以前我认为只有自己独立开发一款 APP 才称得上移动开发。但现在,在支付宝、微信等手机必备软件上去开发,显然可以更容易、快捷获取到用户,也不用去考虑 Android/iOS 平台的差异性,这些平台提供方都帮你解决了,开发者可以更加专注于业务本身的开发,同时也为用户带去了便利,不需要专门再为你的业务再去下载一个 APP。在大前端的时代,Google 家的 Flutter 有着统一移动开发的野心;Web Assembly 的出现让复杂应用在浏览器上运行成为了可能。在新技术的冲击下,小程序可以算得上一处避难的栖息地吧。

# mPaaS

前些年云的概念很火,现在依然热度不减,已经有越来越多的人开始投入的云的怀抱。根据云服务的不同,大致可以分为三类:

  1. IaaS(Infrastructure as a Service)
  2. PaaS(Platform as a Service)
  3. SaaS(Service as a Service)

从字面意义上就可以明白大概是什么意思了,第一个是基础设施服务,第二个是平台服务,第三个是开箱即用的服务,举个简单的例子,比如说我要写一个网站:

# 不采用云服务

像学校这些地方可能就不用云服务,因为里面会有一些数据信息需要放在本地,这个时候要做的步骤差不多就是这样子:

  1. 购买服务器并安装服务器的操作系统
  2. 去网络服务商申请一个固定 IP,方便公网能够访问
  3. 开始编写程序代码,并部署到服务器上

前面的两个步骤也比较繁琐,尤其是购买服务器,一来成本比较高,我大概是去年暑假前向老师说明要采购服务器,但现在也没个影子。不采用云服务的话就需要自己来管理这些内容,比如说存储、网络、虚拟化等等一些情况。

# IaaS

阿里云 10 块钱一个月的学生服务器就是 IaaS 的代表,它提供一个服务器给我们,有公网 IP,有存储,系统也可以任我门选择,不想要了就直接删除,这就是云带来的好处。但是我们还是需要去配置服务器的环境,也要自己编写代码。

# PaaS

环境配置向来是令人头痛的问题,重复、枯燥且乏味。痛点存在的地方,可能就有商机。PaaS 提供服务器软件给我们,我们要做的只需要开发自己的代码就可以了,不再关注如何配置服务器软件的事情。

# SaaS

我什么都不想干怎么办?SaaS 就是一种解决方案。它提供给你需要的东西,比如说你要开一个论坛,你只需要找到一个提供这种服务的提供商,不再需要关心技术的问题,代码怎么写啊,如何维护啊,这些都不需要关心,开箱即用就完事儿了。

为了更清楚地对比这些内容,我找了一张图来:

mPaaS(Mobile PaaS)为 App 开发、测试、运营及运维提供云到端的一站式解决方案,能有效降低技术门槛、减少研发成本、提升开发效率,协助企业快速搭建稳定高质量的移动应用。

mPaaS 指的就是 Mobile PaaS。这也体现了支付宝移动端设计的演进之路,从单一应用到现在的超级 APP。最方便的时它有丰富的组件,直接把支付宝使用的众多组件开放出来,并简化了介入流程,能够让开发者搭积木似的快速搭建自己的 App。这句话是我从官网功能介绍里复制的,但是确实也是 mPaaS 该有的样子。

# 热修复

热修复也称 hotpatch,相当于一个补丁包。正常程序发布的流程是写完代码、测试并确认没问题之后打包发布到应用市场,再由用户主动去手动更新。但有些时候会有一些突发情况,来不及再去走这一个流程,所以热修复也可以说是一次紧急发布。根据蚂蚁金服热修复的文档,紧急发布只用于修复严重的、影响面积大的、具有高可复现性的问题。包括但不仅限于以下情况:

  • 高概率的闪退
  • 严重的 UI 问题
  • 可能造成资损或用户投诉的 Bug
  • 客户端某些功能不能使用
  • 监管审查导致的紧急修改

我们需要在我们开发的 App 中加入具有热修复功能的组件,需要进行热修复的时候再线上发布。

# 环境搭建

根据文档内容搭建好开发环境,需要注意的一点是, mPaaS 对工具的版本有着严格要求,遵照文档所提供的版本安装即可。

# 在控制台创建应用

登录控制台,创建一个 mPass 应用:

mPass

在 mPass 应用中,打开代码管理中的代码配置页面,切换到 Android,填入自定义的 Package Name 并下载配置。

mpaas

# 创建 Android 工程

在根据文档配置好开发环境之后,重新打开 Android Studio 可以看见多了两个选项:

我们选择 New MpaaS Portal Project ,这里要注意的一点是, Package Name 要与控制台中的一样。

这个时候选择刚刚下载的配置文件,会自动解析。理论上来说我应该给一些敏感信息打码的,但是我可以管理控制台,也就是说使用了上面的内容,可能就会被我管理到,其实就是忘记打马赛克了而已。接下来就是一些简单的配置,这里就不再赘述。在点击 Finish 之后,会生成两个工程:

  • 一个工程名字后缀为 Launcher,属于 Bundle 工程
  • 另一个工程属于 Portal 工程

根据蚂蚁金服的文档,模块化是 mPaaS 的核心设计理念。一个基于 mPaas 的框架开发的 App 包括:

  • 一个或多个 Bundle 工程:一个 Bundle 即是一个业务独立的模块
    • 一个 Portal 工程:Portal 负责把所有的 Bundle 构建结果合并成一个可运行的 .apk 包。因此需要先构建各 Bundle,再构建 Portal。Portal 一般不含业务代码,必须有一个 Bundle

在创建完成后可以看到已经有两个项目了,我们选择菜单栏中 mPaaS > Build。先构建 Bundle 工程再构建 Portal 工程,构建完成后可以选择真机进行安装。

# 签名

这里提到的签名指的是对 App 进行签名。那为什么要签名呢?首先,在 Android 中如果安装了两个包名相同的程序,那么新装的会覆盖前一个的,为了防止有些人使用相同的包名来混淆替换已经安装的程序,签名是一种有效的方式。同时,如果 App 中如果任何文件被修改了,那么在安装校验的时候,就会验证失败。也可以说,只要修改了 Apk 中的任何内容,就需要重新签名。这里推荐一个工具 Android Crack Tool,特别方便,做 CTF 逆向的时候经常可以用到,比如反编译等功能。

我们可以在 Android Studio 菜单栏中 Build > Generate Signed APK… 进行 APK 的签名。记住!是在 Portal 工程中进行签名!


这里可以看见有两种签名方式,V2 是安卓 7.0 之后引入的,它能提供更快的应用安装时间和更多针对未授权 APK 文件更改的保护。因为安卓版本的分布比较广泛,为了增强兼容性,我们把 V1 也勾选上。如果出现了应用无法安装的情况,把 V2 去掉试试。签名完成后右下角会弹出如下的对话框,我们点击 locate 即可打开在资源管理器中的安装包文件。

# 重新下载配置文件

现在回到控制台,上传刚刚签好名的安装包,下载新的配置文件。

相较于之前未签名的情况,这次是一个压缩包文件,解压之后有两个文件:

此时我们可以看到,配置文件中的 base64Code 也不再为空。

接下来我们就是要替换配置文件,回到 Portal 工程。在 Android Studio 中左上角,把 Android 切换成 Project:

在 App 目录下替换原先的配置文件,此时 base64Code 中已经有值。

接下来在 App > build.gradle 中添加签名信息:

debug {
            keyAlias 'key0'
            keyPassword '123456'
            storeFile file('/Users/apple/Desktop/Untitled')
            storePassword '123456'
        }

下一步我们删除 App/src/main/res/drawable/yw_1222.jpg 文件,接着我们重新构建这两个工程即可,记住先是 Bundle 工程再是 Portal 工程。如果之前安装了未签名的应用,那现在手机中卸载才能安装签完名的应用。可能会出现资源重复的错误,因为我们之前未签名的应用之间构建过了,已经存在 yw_1222.jpg 这个文件了,去相应的目录删除掉即可。

# 编写 Bug

没错,你没有看错,现在我们要主动去写一段有 Bug 的代码,让程序崩溃,再通过控制台发布补丁文件,在用户无感知的情况下修复这段 Bug。

在 Bundle 工程中的 res > layout > main.xml 中添加两个按钮,可以在 Design 页面中拖动,也可以在 Text 页面中直接编写代码:

<Button
    android:id="@+id/crash"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Crash" />
<Button
    android:id="@+id/hotfix"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Hotfix" />

此时我们已经给两个按钮赋予了 id,后面就可以使用 id 来找到这个元素。

接着我们去到 java > 包名 > MainActivity.java 里面添加如下代码:

/*
 * 找到 id 为 crash 的按钮并绑定点击事件监听器
 */
findViewById(R.id.crash).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int result = 2 / 0; // 除以 0 会让 App 产生崩溃
                Toast.makeText(getApplicationContext(), "result = " + result, Toast.LENGTH_SHORT).show(); // 在 App 下方显示结果的字符串
            }
        });
findViewById(R.id.hotfix).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
      // 触发热修复
        HotPatchUtils.trigDynamicRelease(getApplicationContext(), true);
    }
});

<div align=center>

</div>

在点击了 Crash 按钮之后,程序会崩溃并退出,我们也可以在 Android Studio 中的 Logcat 中查看到崩溃信息。

接着我们设置 Logcat 不使用过滤器(No Fliters)并输入 DynamicRelease 过滤日志。

# 备份 Bug 版本的构建结果

我们对 Bug 版本的构建结果进行进行备份,方便后续热修复使用。需要备份两个文件:

  • 生成的 .apk 文件,它位于 Portal 工程 app/build/outputs/apk/debug 目录下。
  • Launcher 工程的构建结果,它是位于 Bundle 工程 app/build/intermediates/bundle/ 目录下的 demo-build-raw.jar 文件。

# 修复 Bug

我们去到 Bundle 工程中 java > 包名 > MainActivity.java 里面修改上面的代码:

findViewById(R.id.crash).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int result = 2 / 1; // 把 0 修改成 1
                Toast.makeText(getApplicationContext(), "result = " + result, Toast.LENGTH_SHORT).show(); 
            }
        });

接着我们根据先 Bundle 后 Portal 的构建顺序使用 mPaaS 工具进行构建,记住不要把它装到手机里面,那样的话就体验不了热修复了。

# 生成热修复包

打开菜单栏 mPaaS > Generate Hotpatch ,选择新旧 Bundle 文件并选择输出目录,输出目录可以选择方便找到的:

  • New bundle:选择无 Bug 的构建结果中 Bundle 工程中的 .jar 包,路径同上面备份的路径
  • Old bundle:上面备份的有 Bug 的构建结果中的 .jar
  • Patch file dir:选择保存的路径,我保存在桌面方便寻找

填入之前的签名信息即可生成热修复包,这个 .jar 包的大小应该是非 0。

# 控制台发布热修复包

回到控制台,去到 实时发布 > 热修复管理 页面中添加热修复:

上传我们刚刚生成的热修复包,并填写目标版本,创建 mPaaS 工程时,App 默认的版本是 1.0.0.0 ,接着点击确定并选择创建发布,并选择正式发布:

在控制台点击 + 号可以进一步地控制此次热修复的发布:

# App 热修复

这个时候我们回到手机 App,点击 Hotfix 并观察控制台,这个时候我们再次点击 Crash 发现依然会闪退:

但是当我们再次打开这个 App 的时候,点击 Crash 按钮的时候已经能够实现正常的功能了:

# 参考资料

SaaS vs PaaS vs IaaS: What’s The Difference and How To Choose

基于 mPaaS 框架的热修复

適用於開發人員的 Android N