android-热修复-sophix

前言

目前Android业内,热修复技术百花齐放,各大厂都推出了自己的热修复方案,使用的技术方案也各有所异,当然各个方案也都存在各自的局限性。在面对众多的方案,希望通过梳理这些热修复方案的对比及实现原理,掌握热修复技术的本质,同时也对项目接入做好准备。

什么是热修复技术?

关于热修复这个名词,并不陌生。相信大家都有过更新window补丁的经历,通过补丁可以动态修复系统的漏洞,只不过这个过程对用户而言是可选及自行操作。

那么关于Android平台的热修复技术,简单来说,就是通过下发补丁包,让已安装的客户端动态更新,让用户可以不用重新安装APP,就能够修复软件缺陷的一种技术。

为什么要使用热修复技术?

在回答这个问题之前,我觉得应该先思考如下几个问题。

  1. 开发上线的版本能保证不存在Bug么?
  2. 修复后的版本能保证用户都及时更新么?
  3. 如何最大化减少线上Bug对业务的影响?

从这些角度来说,相信大家应该都能有所体会,热修复技术带来的优势不言而喻。

  1. 可快速修复,避免线上Bug带来的业务损失,把损失降到最低。
  2. 保证客户端的更新率,无须用户进行版本升级安装
  3. 良好的用户体验,无感知修复异常。节省用户下载安装成本。

 

怎么选择热修复技术方案?

国内主流的技术方案

1、阿里系

名称说明
AndFix 开源,实时生效
HotFix阿里百川,未开源,免费、实时生效
Sophix未开源,商业收费,实时生效/冷启动修复

HotFix是AndFix的优化版本,Sophix是HotFix的优化版本。目前阿里系主推是Sophix。

2、腾讯系

名称说明
Qzone超级补丁QQ空间,未开源,冷启动修复
QFix手Q团队,开源,冷启动修复
Tinker微信团队,开源,冷启动修复。提供分发管理,基础版免费

3、其他

名称说明
Robust 美团, 开源,实时修复
Nuwa大众点评,开源,冷启动修复
Amigo饿了么,开源,冷启动修复

方案对比

方案对比SophixTinkernuwaAndFixRobustAmigo
类替换yesyesyesnonoyes
So替换yesyesnononoyes
资源替换yesyesyesnonoyes
全平台支持yesyesyesnoyesyes
即时生效同时支持nonoyesyesno
性能损耗较少较小较大较小较小较小
补丁包大小较小较大一般一般较大
开发透明yesyesyesnonoyes
复杂度傻瓜式接入复杂较低复杂复杂较低
Rom体积较小Dalvik较大较小较小较小
成功率较高较高一般最高较高
热度
开源noyesyesyesyesyes
收费收费(设有免费阈值)收费(基础版免费,但有限制)免费免费免费免费
监控提供分发控制及监控提供分发控制及监控nononono

综上所述。我选择了sophix

开始傻瓜式接入:

1.首先去官网登陆创建应用(需要APP包名)

官网地址

2.Project-build.gradle

   maven {
            url "http://maven.aliyun.com/nexus/content/repositories/releases"
        }

3.APP-build.gradle

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

4.Mainfest-添加权限

 //-- 网络权限 -->
    <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" />
    // -- 外部存储读权限,调试工具加载本地补丁需要  6.0注意特殊处理 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

5.Mainfest-application  查看配置-见5.1

  <meta-data
            android:name="com.taobao.android.hotfix.IDSECRET"
            android:value="AppKey" />
        <meta-data
            android:name="com.taobao.android.hotfix.APPSECRET"
            android:value="AppSecret" />
        <meta-data
            android:name="com.taobao.android.hotfix.RSASECRET"
            android:value="RSA" />

5.1应用配置查看

6.新建MyApplication,SophixStubApplication.

package com.example.duqianlong.sophixtest;

import android.app.Application;

/**
 * Created by Duqianlong on 2019/8/22.
 */

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        //自己正常需要做的事情
    }
}
package com.example.administrator.standardOA.application;

/**
 * Created by Duqianlong on 2019/8/21.
 */

import android.content.Context;
import android.os.Looper;
import android.support.annotation.Keep;
import android.util.Log;
import android.widget.Toast;

import com.taobao.sophix.PatchStatus;
import com.taobao.sophix.SophixApplication;
import com.taobao.sophix.SophixEntry;
import com.taobao.sophix.SophixManager;
import com.taobao.sophix.listener.PatchLoadStatusListener;

/**
 * Sophix入口类,专门用于初始化Sophix,不应包含任何业务逻辑。
 * 此类必须继承自SophixApplication,onCreate方法不需要实现。
 * 此类不应与项目中的其他类有任何互相调用的逻辑,必须完全做到隔离。
 * AndroidManifest中设置application为此类,而SophixEntry中设为原先Application类。
 * 注意原先Application里不需要再重复初始化Sophix,并且需要避免混淆原先Application类。
 * 如有其它自定义改造,请咨询官方后妥善处理。
 */
public class SophixStubApplication extends SophixApplication {
    private final String TAG = "SophixStubApplication";
    // 此处SophixEntry应指定真正的Application,并且保证RealApplicationStub类名不被混淆。
    @Keep
    @SophixEntry(MyApplication.class)
    static class RealApplicationStub {}
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
//         如果需要使用MultiDex,需要在此处调用。
//         MultiDex.install(this);
        initSophix();
    }
    private void initSophix() {
        String appVersion = "0.0.0";
        try {
            appVersion = this.getPackageManager()
                    .getPackageInfo(this.getPackageName(), 0)
                    .versionName;
        } catch (Exception e) {
        }
        final SophixManager instance = SophixManager.getInstance();
        instance.setContext(this)
                .setAppVersion(appVersion)
                .setSecretMetaData(null, null, null)
                .setEnableDebug(true)
                .setEnableFullLog()
                .setPatchLoadStatusStub(new PatchLoadStatusListener() {
                    @Override
                    public void onLoad(final int mode, final int code, final String info, final int handlePatchVersion) {
                        Log.e(TAG,"code="+code);
                        // 补丁加载回调通知
                        if (code == PatchStatus.CODE_LOAD_SUCCESS) {
                            // 表明补丁加载成功
//                            Toast.makeText(SophixStubApplication.this, "补丁加载成功!", Toast.LENGTH_SHORT).show();
                            Log.i(TAG, "sophix load patch success!");
                            Log.e(TAG, "Sophix加载补丁成功!");
                        } else if (code == PatchStatus.CODE_LOAD_RELAUNCH) {
                            Looper.prepare();
                            Toast.makeText(SophixStubApplication.this, "补丁预加载成功,重新启动生效!", Toast.LENGTH_SHORT).show();
                            Looper.loop();
                            // 表明新补丁生效需要重启. 开发者可提示用户或者强制重启;
                            // 建议: 用户可以监听进入后台事件, 然后调用killProcessSafely自杀,以此加快应用补丁,详见1.3.2.3
                            // 如果需要在后台重启,建议此处用SharePreference保存状态。
                            Log.i(TAG, "sophix preload patch success. restart app to make effect.");
                            Log.e(TAG, "Sophix预加载补丁成功。重新启动应用程序以生效。");

                        }else if (code==PatchStatus.CODE_REQ_NOUPDATE){
                            //查询阶段没有发现新补丁
                        }
                    }
                }).initialize();
        /**
         * int CODE_LOAD_SUCCESS = 1;//加载阶段, 成功
         int CODE_ERR_INBLACKLIST = 4;//加载阶段, 失败设备不支持
         int CODE_REQ_NOUPDATE = 6;//查询阶段, 没有发布新补丁
         int CODE_REQ_NOTNEWEST = 7;//查询阶段, 补丁不是最新的
         int CODE_DOWNLOAD_SUCCESS = 9;//查询阶段, 补丁下载成功
         int CODE_DOWNLOAD_BROKEN = 10;//查询阶段, 补丁文件损坏下载失败
         int CODE_UNZIP_FAIL = 11;//查询阶段, 补丁解密失败
         int CODE_LOAD_RELAUNCH = 12;//预加载阶段, 需要重启
         int CODE_REQ_APPIDERR = 15;//查询阶段, appid异常
         int CODE_REQ_SIGNERR = 16;//查询阶段, 签名异常
         int CODE_REQ_UNAVAIABLE = 17;//查询阶段, 系统无效
         int CODE_REQ_SYSTEMERR = 22;//查询阶段, 系统异常
         int CODE_REQ_CLEARPATCH = 18;//查询阶段, 一键清除补丁
         int CODE_PATCH_INVAILD = 20;//加载阶段, 补丁格式非法
         //查询阶段的code说明
         int CODE_QUERY_UNDEFINED = 31;//未定义异常
         int CODE_QUERY_CONNECT = 32;//连接异常
         int CODE_QUERY_STREAM = 33;//流异常
         int CODE_QUERY_EMPTY = 34;//请求空异常
         int CODE_QUERY_BROKEN = 35;//请求完整性校验失败异常
         int CODE_QUERY_PARSE = 36;//请求解析异常
         int CODE_QUERY_LACK = 37;//请求缺少必要参数异常
         //预加载阶段的code说明
         int CODE_PRELOAD_SUCCESS = 100;//预加载成功
         int CODE_PRELOAD_UNDEFINED = 101;//未定义异常
         int CODE_PRELOAD_HANDLE_DEX = 102;//dex加载异常
         int CODE_PRELOAD_NOT_ZIP_FORMAT = 103;//基线dex非zip格式异常
         int CODE_PRELOAD_REMOVE_BASEDEX = 105;//基线dex处理异常
         //加载阶段的code说明 分三部分dex加载, resource加载, lib加载
         //dex加载
         int CODE_LOAD_UNDEFINED = 71;//未定义异常
         int CODE_LOAD_AES_DECRYPT = 72;//aes对称解密异常
         int CODE_LOAD_MFITEM = 73;//补丁SOPHIX.MF文件解析异常
         int CODE_LOAD_COPY_FILE = 74;//补丁拷贝异常
         int CODE_LOAD_SIGNATURE = 75;//补丁签名校验异常
         int CODE_LOAD_SOPHIX_VERSION = 76;//补丁和补丁工具版本不一致异常
         int CODE_LOAD_NOT_ZIP_FORMAT = 77;//补丁zip解析异常
         int CODE_LOAD_DELETE_OPT = 80;//删除无效odex文件异常
         int CODE_LOAD_HANDLE_DEX = 81;//加载dex异常
         // 反射调用异常
         int CODE_LOAD_FIND_CLASS = 82;
         int CODE_LOAD_FIND_CONSTRUCTOR = 83;
         int CODE_LOAD_FIND_METHOD = 84;
         int CODE_LOAD_FIND_FIELD = 85;
         int CODE_LOAD_ILLEGAL_ACCESS = 86;
         //resource加载
         public static final int CODE_LOAD_RES_ADDASSERTPATH = 123;//新增资源补丁包异常
         //lib加载
         int CODE_LOAD_LIB_UNDEFINED = 131;//未定义异常
         int CODE_LOAD_LIB_CPUABIS = 132;//获取primaryCpuAbis异常
         int CODE_LOAD_LIB_JSON = 133;//json格式异常
         int CODE_LOAD_LIB_LOST = 134;//lib库不完整异常
         int CODE_LOAD_LIB_UNZIP = 135;//解压异常
         int CODE_LOAD_LIB_INJECT = 136;//注入异常
         * */
    }

    @Override
    public void onCreate() {
        super.onCreate();
                SophixManager.getInstance().queryAndLoadNewPatch();
    }
}

6.1Mainfest-application

7.下载补丁生成工具以及测试工具

点我下载补丁生成工具

点我下载android补丁包测试工具

8.上传补丁包

8.1.本地测试补丁包

8.2.新建发布-灰度发布/全量发布

9.打开有问题的apk,开始热修复

如果生成补丁包的时候选择了强行使用冷启动,预下载补丁包后,需要重新启动生效。

10.修复结果

11.需要注意的

1.Windows下运行补丁生成工具,需要安装Java环境且在JDK7或以上才能正常使用。

2.android studio打包的时候需要关闭 Instant Run . 如图

3.如果同一个版本需要发布两个或者多个补丁包,第二次或者第N次生成补丁包的时候,旧包依旧用第一个版本的包。

4.Sophix不能修改AndroidManifest.xml文件,不能修改资源文件,不能新增四大组件,不能修改版本号,不能。所以Sophix更适合于修复致命bug。如果是增加或修改功能等操作,还是发布新版本比较好。

5.把RealApplicationStub的SophixEntry注解的内容改为自己原先真正的MyRealApplication类。

5.1:AndroidManifest里面的application改为新增的SophixStubApplication入口类。

 

 GitHub 地址(点我)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值