阿里Sophix 首次接入

一、前言:

1、简介:

深入探索Android热修复技术原理这本书主要讲解了Android的热修复中的热部署,冷部署以及资源和so库的修复技巧。全文主要讲Sophix应对以上四个方面的技术解析,不管是自家产品还是业界其他方案的横纵对比,Sophix技术目前都是最优的。

1、补丁小,合成不占太多空间和性能。
2、对代码的侵入小,对native代码的hook也精简,做到最大兼容。
3、支持的修复范围广。支持小范围的即时生效和大范围的冷启动。也支持so库和资源修复。

2、Sophix同时使用了热启动的底层替换方案及冷启动的类加载方案,两个方案使用的补丁是相同的。优先热启动。

图片.png

二、阿里云注注册

1、首先去官网注册并登录账号,并进入控制台创建应用,获得相关的AppId,AppSecret,RSA密钥

图片.png

2、如果刚注册,必须进行实名认证,才能使用。

图片.png

3、进入控制台

图片.png

三、集成阿里云热修复

1. 添加工程依赖

1、android studio集成方式

gradle远程仓库依赖, 打开项目找到app的build.gradle文件,添加如下配置:

添加maven仓库地址:

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

2、添加gradle坐标版本依赖:

    android {
        ......
        defaultConfig {
            applicationId "com.xxx.xxx" //包名
            ......
            ndk {
                //选择要添加的对应cpu类型的.so库。
                //热修复支持五种
                abiFilters 'arm64-v8a', 'armeabi', 'armeabi-v7a', 'x86', 'x86_64'
            }
            ......
        }
        ......
    }
    dependencies {
        ......
            compile 'com.aliyun.ams:alicloud-android-hotfix:3.2.15'
        ......
    }

2. 添加应用权限

Sophix SDK使用到以下权限,使用maven依赖或者aar依赖可以不用配置。具体配置在AndroidManifest.xml中。

    
    
    
    
    
    

READ_EXTERNAL_STORAGE权限属于Dangerous Permissions,仅调试工具获取外部补丁需要,不影响线上发布的补丁加载,调试时请自行做好android6.0以上的运行时权限获取。

3. 配置AndroidManifest文件

AndroidManifest.xml中间的application节点下添加如下配置:

    
    
    

将上述value中的值分别改为通过平台HotFix服务申请得到的App Secret和RSA密钥,出于安全考虑,建议使用setSecretMetaData这个方法进行设置,详见SDK API的方法说明。如找不到对应参数,可参考EMAS快速入门>下载配置文件获取应用配置信息。

4. 混淆配置

    #基线包使用,生成mapping.txt
    -printmapping mapping.txt
    #生成的mapping.txt在app/build/outputs/mapping/release路径下,移动到/app路径下
    #修复后的项目使用,保证混淆结果一致
    #-applymapping mapping.txt
    #hotfix
    -keep class com.taobao.sophix.**{*;}
    -keep class com.ta.utdid2.device.**{*;}
    -dontwarn com.alibaba.sdk.android.utils.**
    #防止inline
    -dontoptimize

5. 初始化

初始化的调用应该尽可能的早,必须在Application.attachBaseContext()的最开始(在super.attachBaseContext之后,如果有Multidex,也需要在Multidex.install之后)进行SDK初始化操作,初始化之前不能用到其他自定义类,否则极有可能导致崩溃。而查询服务器是否有可用补丁的操作可以在后面的任意地方。不建议在Application.onCreate()中初始化,因为如果带有ContentProvider,就会使得Sophix初始化时机太迟从而引发问题。

Sophix最新版本引入了新的初始化方式。

原来的初始化方式仍然可以使用。只是新方式可以提供更全面的功能修复支持,将会带来以下优点:

初始化与应用原先业务代码完全隔离,使得原先真正的Application可以修复,并且减少了补丁预加载时间等等。

腾讯的Application (SophixStubApplication )

    package com.my.pkg;
    import android.app.Application;
    import android.content.Context;
    import android.support.annotation.Keep;
    import android.util.Log;
    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;
    import com.my.pkg.MyRealApplication;
    /**
     * 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(BaseApplication .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) {
                            if (code == PatchStatus.CODE_LOAD_SUCCESS) {
                                Log.i(TAG, "sophix load patch success!");
                            } else if (code == PatchStatus.CODE_LOAD_RELAUNCH) {
                                // 如果需要在后台重启,建议此处用SharePreference保存状态。
                                Log.i(TAG, "sophix preload patch success. restart app to make effect.");
                            }
                        }
                    }).initialize();
        }
    }

自己原有的Application

public class BaseApplication extends Application {
    private static Context mInstance;
    private static String uniqueID;
    private final String TAG = "BaseApplication";

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
        //查询下载补丁包
        SophixManager.getInstance().queryAndLoadNewPatch();
        initData();
    }
 }

注意:获取版本号,开始下载安装补丁包(可以放在自己的Application中的onCreate()中)

    // queryAndLoadNewPatch不可放在attachBaseContext 中,否则无网络权限,建议放在后面任意时刻,如onCreate中
    SophixManager.getInstance().queryAndLoadNewPatch();

这其中,关键一点是:

        @Keep
        @SophixEntry(BaseApplication .class)
        static class RealApplicationStub {}

SophixEntry应指定项目中原先真正的Application(原项目里application的android::name指定的),这里用BaseApplication 指代。并且保证BaseApplication Stub类名不被混淆。而SophixStubApplication的类名和包名可以自行取名。

这里的Keep是android.support包中的类,目的是为了防止这个内部静态类的类名被混淆,因为sophix内部会反射获取这个类的SophixEntry。如果项目中没有依赖android.support的话,就需要在progurad里面手动指定RealApplicationStub不被混淆,详见下文。

然后,在proguard文件里面需要加上下面内容:

    -keepclassmembers class com.my.pkg.BaseApplication {
        public ();
    }
    -keep class com.my.pkg.SophixStubApplication$BaseApplication Stub

目的是防止真正Application的构造方法被proguard混淆。

最后,需要把AndroidManifest里面的application改为这个新增的SophixStubApplication类:

        
            ... ...

这样便完成了新方式的初始化接入改造。

6、总结一下,过程一共有四个步骤:

1、把此SophixStubApplication入口类添加进项目中,所有Sophix相关初始化放在此类中。并且不应包含开发者的任何业务逻辑代码。 若使用了MultiDex,也应在SophixStubApplication的initSophix之前添加,并且需要记得在原来的Application里面去除MultiDex,避免重复调用导致问题。
2、 把RealApplicationStub的SophixEntry注解的内容改为自己原先真正的MyRealApplication类。
3、混淆文件中确保某些内容不被混淆。
4、AndroidManifest里面的application改为新增的SophixStubApplication入口类。

7、常见状态码:

一个补丁的加载一般分为三个阶段: 查询、预加载、加载,各个阶段的常见状态码如下:

        //兼容老版本的code说明
        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_REQ_TOOFAST = 19;//连续两次请求不能小于3s
        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;//注入异常

四、生成补丁包

1、下载打包工具:

Mac版本打包工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/SophixPatchTool_macos.zip

Windows版本打包工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/SophixPatchTool_windows.zip

Linux版本打包工具地址:http://ams-hotfix-repo.oss-cn-shanghai.aliyuncs.com/SophixPatchTool_linux.zip

2、 生成补丁包

1、点击软件首次打开


图片.png

2、点击设置,然后配置签名信息,指定补丁包的输出路径(只第一次需要配置这些信息)


图片.png

3、直接选择旧包和新包(旧包指有bug的包,新包是修复完bug的包)
然后点击 GO!
补丁包就会生成在你指定的输出路径下:


图片.png
图片.png

4、然后进入控制台,添加版本,上传补丁。
注意,版本号必须和项目保持一致。

// 默认版本号是 1.0,会获取当前版本

   String appVersion;
        try {
            appVersion = this.getPackageManager().getPackageInfo(this.getPackageName(), 0).versionName;
        } catch (Exception e) {
            appVersion = "1.0.0";
        }

        SophixManager.getInstance().setContext(this)
                .setAppVersion(appVersion)

项目中的版本号( build.gradle 中的 versionName )

  defaultConfig {
        applicationId "com.risecenter.rise_online_android"
        minSdkVersion 21
        targetSdkVersion 26
        versionCode 1
        versionName "1.0.0"
    }

补丁后台的版本也要一致:


图片.png

依次进行下列步骤即可:


图片.png
图片.png
图片.png

灰度发布:可指定修复补丁的手机数量
全量发布:用于生产环境。经本地测试,灰度发布测试没问题后,就可以全量发布了

特别注意:发布完毕,杀掉进程(而不是返回)才会生效。


  • 链接:https://www.jianshu.com/p/143a2e08fee9
  • 链接:https://blog.csdn.net/qq_33337504/article/details/77864348
  • 链接:https://help.aliyun.com/document_detail/180957.html?spm=a2c4g.11186623.6.574.59eec464rgkFsH

你可能感兴趣的:(阿里Sophix 首次接入)