Android热修复-Sophix

955 阅读6分钟

Android

画中的android

以往当Android App出现bug的时候,甚至仅仅是修改一行代码,都要重新发布新版本对bug进行修复,这样带来的缺点是明显的,需要用户重新升级app,覆盖率太慢,成本太高。所以就出现了热修复技术,通过打补丁的方式,通过从服务器下载补丁包,然后对有问题的类中出问题的方法,进行替换,优点是用户无感知修复,无需下载新的应用,代价小。对比其他的热修复方案,来耍一耍阿里-Sophix

介绍

“冷热”

  • 插件化 - apk 分为宿主和插件部分,插件在需要的时候才加载进来
  • 热修复 – 更新的类或者插件粒度较小的时候,我们会称之为热修复,一般用于修复bug
  • 热更新 – 2016 Google 的 Android Studio 推出了Instant Run 功能 同时提出了3个名词
  • 热部署 – 方法内的简单修改,无需重启app和Activity。
  • 暖部署 – app无需重启,但是activity需要重启,比如资源的修改。
  • 冷部署 – app需要重启,比如继承关系的改变或方法的签名变化等。

热修复特点

  • 无需重新发版,实时高效热修复
  • 用户无感知修复,无需下载新的应用,无需重装App,代价小
  • 修复成功率高,把损失降到最低

阿里热修复方案对比

方案对比 Andfix开源版本 阿里Hotfix 1.X 阿里Hotfix最新版 (Sophix)
方法替换 支持,除部分情况[0] 支持,除部分情况 全部支持
方法增加减少 不支持 不支持 以冷启动方式支持[1]
方法反射调用 只支持静态方法 只支持静态方法 以冷启动方式支持
即时生效 支持 支持 视情况支持[2]
多DEX 不支持 支持 支持
资源更新 不支持 不支持 支持
so库更新 不支持 不支持 支持
Android版本 支持2.3~7.0 支持2.3~6.0 全部支持包含7.0以上
已有机型 大部分支持[3] 大部分支持 全部支持
安全机制 加密传输及签名校验 加密传输及签名校验
性能损耗 低,几乎无损耗 低,几乎无损耗 低,仅冷启动情况下有些损耗
生成补丁 繁琐,命令行操作 繁琐,命令行操作 便捷,图形化界面
补丁大小 不大,仅变动的类 小,仅变动的方法 不大,仅变动的资源和代码[4]
服务端支持 支持服务端控制[5] 支持服务端控制

说明:

  • [0] 部分情况指的是构造方法、参数数目大于8或者参数包括long,double,float基本类型的方法。
  • [1] 冷启动方式,指的是需要重启app在下次启动时才能生效。
  • [2] 对于Andfix及Hotfix 1.X能够支持的代码变动情况,都能做到即时生效。而对于其他代码变动较大的情况,会走冷启动方式,此时就无法做到即时生效。
  • [3] Hotfix 1.X已经支持绝大部分主流手机,只是在X86设备以及修改了虚拟机底层结构的ROM上不支持。
  • [4] 由于支持了资源和库,如果有这些方面的更新,就会导致的补丁变大一些,这个是很正常的。并且由于只包含差异的部分,所以补丁已经是最大程度的小了。
  • [5] 提供服务端的补丁发布和停发、版本控制和灰度功能,存储开发者上传的补丁包。

其他热修复方案

方案 作者
Tinker 微信(apk补丁)
Robust 美团
Amigo 饿了么(apk补丁)
Nuwa 个人开发者
Dexposed
RocooFix 个人开发者

集成Sophix

注册阿里云账号

创建App文档传送门

  • 注册完开发者账号,成功登录后进入控制台,添加移动热修复服务。
    添加移动热修复
  • 开通热修复服务后,跳转到热修复产品界面-App管理,创建App “Sophix测试”,创建完成后会出现两个平台的App列表:iOS和Android
    Sophix测试

  • 点击管理进入Android平台,在客户端里,需要使用到AppId、APPSecret、RSA密钥
    Sophix必要信息

客户端集成

Sophix文档传送门

  • 引入maven依赖仓库
    在项目app下的build.gradle中添加maven仓库地址和版本依赖

    • 添加maven仓库地址:

      repositories {
         maven {
             url "http://maven.aliyun.com/nexus/content/repositories/releases"
         }
      }
    • 添加依赖:

      compile 'com.aliyun.ams:alicloud-android-hotfix:3.1.2'

引入Sophix依赖

  • 添加使用权限

    <! -- 网络权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <! -- 外部存储读权限,调试工具加载本地补丁需要 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
  • 配置AndroidManifest文件
    在application节点里添加配置,用之前在阿里云上创建的App的配置信息AppId、APPSecret、RSA密钥替换value的值:

    <application>
    	<meta-data
    	android:name="com.taobao.android.hotfix.IDSECRET"
    	android:value="App ID" />
    	<meta-data
    	android:name="com.taobao.android.hotfix.APPSECRET"
    	android:value="App Secret" />
    	<meta-data
    	android:name="com.taobao.android.hotfix.RSASECRET"
    	android:value="RSA密钥" />
    	···
    </application>

可参考官方的动图示例:
gif-Sophix

  • Application接入SDK
    其他接口使用请查看SDK文档
    /**
     * 初始化Sophix
     */
    private void initSophix()
    {
    	String appVersion;
    	try
    	{
    		appVersion = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
    	}
    	catch (Exception e)
    	{
    		e.printStackTrace();
    		appVersion = "1.0.0";
    	}
    	SophixManager.getInstance().setContext(this).setAppVersion(appVersion).setAesKey(null).setEnableDebug(true).setPatchLoadStatusStub(new PatchLoadStatusListener()
    	{
    		@Override
    		public void onLoad(final int mode, final int code, final String info, final int handlePatchVersion)
    		{
    			// 补丁加载回调信息
    			StringBuilder msg = new StringBuilder();
    			msg.append("Mode:").append(mode).append("\n");
    			msg.append("Code:").append(code).append("\n");
    			msg.append("Info:").append(info).append("\n");
    			msg.append("HandlePatchVersion:").append(handlePatchVersion).append("\n");
    			if (mDisplayListener != null)
    				mDisplayListener.handle(msg.toString());
    			// 补丁加载回调通知
    			switch (code)
    			{
    			case PatchStatus.CODE_LOAD_SUCCESS:
    				// 表明补丁加载成功
    				break;
    			case PatchStatus.CODE_LOAD_RELAUNCH:
    				// 表明新补丁生效需要重启. 开发者可提示用户或者强制重启;
    				// 建议: 用户可以监听进入后台事件,然后调用killProcessSafely自杀
    				// 注意:不可以直接Process.killProcess(Process.myPid())来杀进程,这样会扰乱Sophix的内部状态。
    				break;
    			case PatchStatus.CODE_LOAD_FAIL:
    				// 内部引擎异常,推荐此时清空本地补丁,防止失败补丁重复加载
    				SophixManager.getInstance().cleanPatches();
    				break;
    			default:
    				// 其它错误信息,查看PatchStatus类说明
    				break;
    			}
    		}
    	}).initialize();
    	// 加载新的补丁包
    	SophixManager.getInstance().queryAndLoadNewPatch();
    }

至此,Sophix配置完成。

测试

  • 客户端第一版本,如图,打包成Sophix_V1.apk
    Sophix_V1
  • 客户端补丁版本,如图,打包成Sophix_V2.apk
    Sophix_V2

  • 生成补丁,生成补丁文档传送门
    Windows下载阿里补丁工具SophixPatchTool,运行SophixPatchTool.exe,添加包,如果有签名等设置,则点击设置,配置相应的签名等,然后点击“Go”,生成补丁,即sophix-patch.jar
    Sophix补丁生成

    生成补丁

  • 上传补丁
    进入阿里云热修复App管理的Android平台里,即查看AppId、APPSecret、RSA密钥那个页面,添加新版本,成功添加版本后,点击查看详情进入,上传刚刚生成的sophix-patch.jar补丁。
    Sophix添加补丁版本

    添加补丁版本


    Sophix上传补丁

    上传补丁

  • 本地测试
    安装Sophix_V1.apk,同时将补丁sophix-patch.jar放到Android设备的目录里:/sdcard/sophix-patch.jar,下载hotfixdebug工具,安装后,打开进入调试apk,配置如下:
    Sophix本地测试

测试成功
Sophix本地测试成功

  • 发布
    进入阿里云的补丁详情页面,点击发布。再次进行调试,这次不用Sophix调试工具;先卸载已安装的Sophix_V1,重新安装,打开后再次等待检测补丁更新,再次出现提示ok。
    Sophix发布

推广

有兴趣的童鞋可以看看阿里出品的热修复原理宝典

阿里热修复原理宝典