欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android Bugly Tinker 热更新

程序员文章站 2024-01-22 11:41:58
...

Bugly的热更新使用记录


背景

最近研究热Android方面的热更新,写了一个小demo已经测试成功了,趁热打铁记录一下

很久以前研究过使用tinker,无奈能力有限,一直配置不清楚,遂放弃,后来看到了Bugly的热更新也在使用tinker,而且已经有控制台,版本控制下发等功能,无需自己再写后台 , bugly将集成tinker的很多麻烦的配置写成了tinker-support的脚本,使用起来也很方便

前排丢一发bugly热更新文档指南传送门

工具 AndroidStudio


步骤

第一步,创建一个普通项目
Android Bugly Tinker 热更新


第二步,配置gradle

project下的build.gradle

// tinkersupport插件, 其中lastest.release指拉取最新版本,也可以指定明确版本号,例如1.0.4
        classpath "com.tencent.bugly:tinker-support:1.1.5"

Android Bugly Tinker 热更新
主Model下的build.gradle

 implementation "com.android.support:multidex:1.0.1" // 多dex配置
    //注释掉原有bugly的仓库
    //compile 'com.tencent.bugly:crashreport:latest.release'//其中latest.release指代最新版本号,也可以指定明确的版本号,例如1.3.4
    implementation 'com.tencent.bugly:crashreport_upgrade:1.3.6'
    // 指定tinker依赖版本(注:应用升级1.3.5版本起,不再内置tinker)
    implementation 'com.tencent.tinker:tinker-android-lib:1.9.9'
    implementation 'com.tencent.bugly:nativecrashreport:latest.release' //其中latest.release指代最新版本号,也可以指定明确的版本号,例如2.2.0

Android Bugly Tinker 热更新
因为tinker还需要配置很多东西,即使bugly帮我们封装了,配置项依然很多,所以在主Model的目录下创建 tinker-support.gradle 文件,然后在主Model中使用它

创建该文件,以下代码粘进去,bugly帮我们封装好的代码
Android Bugly Tinker 热更新


apply plugin: 'com.tencent.bugly.tinker-support'

def bakPath = file("${buildDir}/bakApk/")

/**
 * 此处填写每次构建生成的基准包目录
 */
def baseApkDir = "app-0119-11-26-19"

/**
 * 对于插件各参数的详细解析请参考
 */
tinkerSupport {

    // 开启tinker-support插件,默认值true
    enable = true

    // 指定归档目录,默认值当前module的子目录tinker
    autoBackupApkDir = "${bakPath}"

    // 是否启用覆盖tinkerPatch配置功能,默认值false
    // 开启后tinkerPatch配置不生效,即无需添加tinkerPatch
    overrideTinkerPatchConfiguration = true

    // 编译补丁包时,必需指定基线版本的apk,默认值为空
    // 如果为空,则表示不是进行补丁包的编译
    // @{link tinkerPatch.oldApk }
    baseApk = "${bakPath}/${baseApkDir}/app-release.apk"

    // 对应tinker插件applyMapping
//    baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"

    // 对应tinker插件applyResourceMapping
//    baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"

    // 构建基准包和补丁包都要指定不同的tinkerId,并且必须保证唯一性
    tinkerId = "1.0.1-patch"

    // 构建多渠道补丁时使用
    // buildAllFlavorsDir = "${bakPath}/${baseApkDir}"

    // 是否启用加固模式,默认为false.(tinker-spport 1.0.7起支持)
    // isProtectedApp = true

    // 是否开启反射Application模式
    enableProxyApplication = false

    // 是否支持新增非export的Activity(注意:设置为true才能修改AndroidManifest文件)
    supportHotplugComponent = true

}

/**
 * 一般来说,我们无需对下面的参数做任何的修改
 * 对于各参数的详细介绍请参考:
 * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
 */
tinkerPatch {
    //oldApk ="${bakPath}/${appName}/app-release.apk"
    ignoreWarning = false
    useSign = true
    dex {
        dexMode = "jar"
        pattern = ["classes*.dex"]
        loader = []
    }
    lib {
        pattern = ["lib/*/*.so"]
    }

    res {
        pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
        ignoreChange = []
        largeModSize = 100
    }

    packageConfig {
    }
    sevenZip {
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
//        path = "/usr/local/bin/7za"
    }
    buildConfig {
        keepDexApply = false
        //tinkerId = "1.0.1-base"
        //applyMapping = "${bakPath}/${appName}/app-release-mapping.txt" //  可选,设置mapping文件,建议保持旧apk的proguard混淆方式
        //applyResourceMapping = "${bakPath}/${appName}/app-release-R.txt" // 可选,设置R.txt文件,通过旧apk文件保持ResId的分配
    }
}

主Model的build.gradle中使用该文件,在最上方添加如下代码,名字不要写错

apply from: 'tinker-support.gradle'

这时候同步以下代码,基本的配置已经完成了


第三步,创建MyApplication和MyApplicationLike

官方有两种初始化SDK方法,推荐使用这种,因为反射会影响性能~~~

前面的tinker-support配置中有这一项,确认关掉,然后用application的方法初始化

   // 是否开启反射Application模式
    enableProxyApplication = false

平时我们只会有一个MyApplication,当使用bugly集成的tinker的热更新后,我们要以他的TinkerApplication为基准,而MyApplicationLike类是以往的MyApplication的代理类,后续如果初始化,可以放在这里

参数1:tinkerFlags 表示Tinker支持的类型 dex only、library only or all suuport,default: TINKER_ENABLE_ALL
参数2:delegateClassName Application代理类 这里填写你自定义的ApplicationLike
参数3:loaderClassName Tinker的加载器,使用默认即可
参数4:tinkerLoadVerifyFlag 加载dex或者lib是否验证md5,默认为false

public class MyApplication extends TinkerApplication {
    public MyApplication() {
        super(ShareConstants.TINKER_ENABLE_ALL, BuildConfig.APPLICATION_ID + ".MyApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader", false);
    }
}

public class MyApplicationLike extends DefaultApplicationLike {
    public MyApplicationLike(Application application, int tinkerFlags,
                             boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime,
                             long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId
        // 调试时,将第三个参数改为true,打印日志
        Bugly.init(getApplication(),"这里放bugly平台申请的appid",true);
    }


    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        // you must install multiDex whatever tinker is installed!
        MultiDex.install(base);

        // 安装tinker
        Beta.installTinker(this);
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
        getApplication().registerActivityLifecycleCallbacks(callbacks);
    }

}

权限肯定是必须的

  <!--    bugly permission-->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <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_LOGS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

myapplication也是一定要用上的

后面的BetaActivity是bugly的Activity建议也注册了,如果要做自定义的升级弹窗会用到,不过,如果用不到不加也不会报错
Android Bugly Tinker 热更新


第四步,打基准包

这个基准包就是出问题的安装包,后面打的补丁包就是为了去修复这个基准包
Android Bugly Tinker 热更新
运行该脚本,会将包打到tinkerSupport配置的路径中
Android Bugly Tinker 热更新
先将安装包安装到手机上,启动之后,看打印日志,上传本地版本到服务器,后续的补丁包会根据上传的信息匹配所属的应用
Android Bugly Tinker 热更新


第五步,修改bug,打补丁包

按照所需,修改项目中的bug,修改代码之后,准备打补丁包
首先进行补丁包配置,到tinker-support,gradle中修改即可

第一处是baseApkDir,这里是指定要合并补丁包的位置,打基准包的时候,每一次编译打包左侧会自动生成文件夹,打补丁包的时候,一定要找到你要更新的那一版,然后在右侧的地方对应上
Android Bugly Tinker 热更新
第二处,修改tinkerid,该id一定要是唯一的
补充,如果不实用多渠道,加固,混淆的,注释掉,不然打补丁包的时候会报错
Android Bugly Tinker 热更新

找到gradle中的tinker-suppport脚本,运行,左侧outputs的patch文件中会直接生成补丁包
Android Bugly Tinker 热更新
如图生成三个文件,上传的补丁包是7zip的文件,上传之前可以先查看一下补丁包
Android Bugly Tinker 热更新
双击打开,查看YAPATCH.MF文件,该文件是tinker-support针对bugly生成的配置,From代表基准包配置的tinkerId , To代表补丁包的tinkerId,都没问题之后,就可以上传了


第六步,上传补丁包
最简单的就是全量更新,加上备注,发布即可,bugly还可以修改下发数量,停止下发,撤回等操作!
Android Bugly Tinker 热更新


第七步,更新补丁包
补丁包需要应用杀死后重启,经过检测补丁包版本后,决定是否下载合并,如果有新发的补丁包,app会消耗少许时间下载文件,当再次重启之后,就会发现已经更新了

成功的时候会看到patch download success的日志,控制台也会显示已下发
Android Bugly Tinker 热更新
Android Bugly Tinker 热更新


结束

此文章用于记录,如想了解更详细可以查看文章开始的bugly官方链接

另外如果有时间,看看官方出的教学视频还是很不错的,官方文档的链接失效了,下面是我到腾讯视频上找到的链接

bugly集成tinker指南

课程1:Bugly热更新介绍
https://v.qq.com/x/page/w0384j4xrnd.html

课程2:tinker-support插件使用
https://v.qq.com/x/page/o03855ejzf4.html

课程3:集成升级SDK
https://v.qq.com/x/page/e03855m02j8.html

课程4:生成补丁包之痛我懂你
https://v.qq.com/x/page/i0385n7ncro.html

课程5:补丁包为何上传不上去?
https://v.qq.com/x/page/e0385shcfzm.html

课程6:普通打包的热更新、
https://v.qq.com/x/page/k0385qx3tk2.html

课程7:渠道包的热更新
https://v.qq.com/x/page/z0393rt2cmq.html

课程8:加固的热更新
https://v.qq.com/x/page/p0398b38vwl.html

综上


补充

后面发现打一个补丁的没问题,如果一个补丁并没有修复bug,还需要在打第二个,可以同第一种方法,但是后面测试打第三个补丁的时候,发现无法打补丁包,错误当时提示的是这个
Warning:ignoreWarning is false, but resources.arsc is changed, you should use applyResourceMapping
后来发现应该是build的文件,某些地方起了冲突,clear一下项目即可,但是 但是 但是 !!! clearn之后的build文件中是空的,以前打过的基准包被清理了,而打补丁包是要基于新包和老包对比差异然后产生补丁包,因此又报了oldapk不存在的问题,所以建议每次打完包之后保留一下基准包的备份
反向思维,将tinker-support中的baseApkDir复制一下作为名称,在bakApk中新建一个文件夹,把老的基准包复制进去,再从新打补丁包即可~!