基于tinker的bugly的集成之路和总结

bugly的简单介绍

1、热更新能力是Bugly为解决开发者紧急修复线上bug,而无需重新发版让用户无感知就能把问题修复的一项能力。Bugly目前采用微信Tinker的开源方案,开发者只需要集成我们提供的SDK就可以实现自动下载补丁包、合成、并应用补丁的功能,我们也提供了热更新管理后台让开发者对每个版本补丁进行管理。
详细文档:https://bugly.qq.com/docs/user-guide/instruction-manual-android-hotfix/?v=20170912151050

基于tinker的bugly的集成之路和总结_第1张图片
image.png

2、bugly的有点:
无需关注Tinker是如何合成补丁的
无需自己搭建补丁管理后台
无需考虑后台下发补丁策略的任何事情
无需考虑补丁下载合成的时机,处理后台下发的策略
我们提供了更加方便集成Tinker的方式
我们通过HTTPS及签名校验等机制保障补丁下发的安全性
丰富的下发维度控制,有效控制补丁影响范围
我们提供了应用升级一站式解决方案

3、bugly的缺点:
无法即时生效,一定要冷启动
只支持部分加固(360)
升级和补丁同时下发,优先处理升级

接入流程

1、首先登陆地址:https://bugly.qq.com/v2/index, 注册登陆bugly控制台,创建自己的产品,获取appid (这个id后面会用到)。根据官方文档接入,基本上没什么大问题,我接入的是改造application,因为官方介绍说这种兼容性比较好,如果文档看的迷迷糊糊的话,里面也贴出视频地址,可以看着视频来接入更直观。

基于tinker的bugly的集成之路和总结_第2张图片
image.png

2、在app目录下创建tinker-support.gradle,里面需要注意的是打补丁包需要改动的是def baseApkDir = “基准包的目录”, tinkerId = “”;不过初试者的话建议看下视频,会更了解里面的参数含义。

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

// 基准包目录
def bakPath = file("${buildDir}/bakApk/")

/**
 * 此处填写每次构建生成的基准包目录
 */
def baseApkDir = "app-1102-17-29-08"

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

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

    // 自动生成tinkerId, 你无须关注tinkerId,默认为false
    autoGenerateTinkerId = false

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

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

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

    // 对应tinker插件applyMapping, 映射文件需要开启混淆才能生成
    // app gradle 里面的 minifyEnabled 设置true才能生成mapping混淆
    baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-debug-mapping.txt"

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

    // 构建基准包跟补丁包都要修改tinkerId,主要用于区分, 基准包:1.0.1-base, 补丁包:1.0.1-patch
    tinkerId = "patch-1.0.1"

    // 打多渠道补丁时指定目录
    // buildAllFlavorsDir = "${bakPath}/${baseApkDir}"

    // 是否使用加固模式,默认为false
    // isProtectedApp = true

    // 是否采用反射Application的方式集成,无须改造Application, 反射兼容性没有改造的好
    // 改造设置false, 试用反射设置true
    enableProxyApplication = false

}

/**
 * 一般来说,我们无需对下面的参数做任何的修改
 * tinkerPatch 主要是用来当enableProxyApplication = true的时候反射集成,如果false此段则不调用
 * 对于各参数的详细介绍请参考:
 * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
 */
tinkerPatch {
    tinkerEnable = true
    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 = "base-2.0.1"
    }
}

3、配置项目下的gradle 和 app 目录下的gradle
项目下的gradle, 以来 classpath 'com.tencent.bugly:tinker-support:1.0.8',这个版本可以根据自己需要选择一个稳定版本,也可以设置latest.release版本

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'
        // 只需配置tinker-support插件依赖,无需再依赖tinker插件
        classpath 'com.tencent.bugly:tinker-support:1.0.8'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

app gradle: 这里面主要是添加依赖 compile 'com.tencent.bugly:crashreport_upgrade:latest.release'(应用升级和热更新都是用这个sdk), 不要忘了依赖下插件脚本 apply from: 'tinker-support.gradle', 至于其他的配置ndk里一般设置 abiFilters 'armeabi' , 'x86'这两个兼容够用了。我这里的是debug测试的,所以没有配release版,一般上线都会配置release版的。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.2"
    defaultConfig {
        applicationId "com.example.administrator.buglyupdatedemo"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        ndk {
            //设置支持的SO库架构
            abiFilters 'armeabi' , 'x86' //, 'armeabi-v7a', 'x86_64', 'arm64-v8a'
        }

        multiDexEnabled true  // 分dex包
    }
    buildTypes {
        release {
            minifyEnabled false  // 开启混淆,生成mapping文件
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            debuggable true
            minifyEnabled false
            signingConfig signingConfigs.debug
        }
    }

    // 编译选项
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }

    // recommend
    dexOptions {
        jumboMode = true
    }

    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }

    repositories {
        flatDir {
            dirs 'libs'
        }
    }

    lintOptions {
        checkReleaseBuilds false
        abortOnError false
    }

}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:26.+'
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
    testCompile 'junit:junit:4.12'
    //注释掉原有bugly的仓库
    //compile 'com.tencent.bugly:crashreport:latest.release'//其中latest.release指代最新版本号,也可以指定明确的版本号,例如2.3.2
    compile 'com.tencent.bugly:crashreport_upgrade:latest.release'//其中latest.release指代最新版本号,也可以指定明确的版本号,例如1.2.0
//    compile 'com.tencent.bugly:nativecrashreport:latest.release' //其中latest.release指代最新版本号,也可以指定明确的版本号,例如2.2.0
    // 多dex配置
    compile "com.android.support:multidex:1.0.1"
}

// 依赖插件脚本
apply from: 'tinker-support.gradle'

4、改造application:新建一个MyApplicationLike 集成 DefaultApplicationLike oncreate方法中初始化bugly, onBaseContextAttached方法中安装MultiDex和tinker

package com.example.administrator.buglyupdatedemo;

import android.annotation.TargetApi;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.multidex.MultiDex;
import android.widget.Toast;

import com.tencent.bugly.Bugly;
import com.tencent.bugly.beta.Beta;
import com.tencent.bugly.beta.interfaces.BetaPatchListener;
import com.tencent.tinker.loader.app.DefaultApplicationLike;

import java.util.Locale;

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();
        Beta.autoCheckUpgrade  = true;
        // 设置是否开启热更新能力,默认为true
        Beta.enableHotfix = true;
        // 设置是否自动下载补丁,默认为true
        Beta.canAutoDownloadPatch = true;
        // 设置是否自动合成补丁,默认为true
        Beta.canAutoPatch = true;
        // 设置是否提示用户重启,默认为false
        Beta.canNotifyUserRestart = true;
        // 补丁回调接口
        Beta.betaPatchListener = new BetaPatchListener() {
            @Override
            public void onPatchReceived(String patchFile) {
                Toast.makeText(getApplication(), "补丁下载地址" + patchFile, Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDownloadReceived(long savedLength, long totalLength) {
                Toast.makeText(getApplication(),
                        String.format(Locale.getDefault(), "%s %d%%",
                                Beta.strNotificationDownloading,
                                (int) (totalLength == 0 ? 0 : savedLength * 100 / totalLength)),
                        Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDownloadSuccess(String msg) {
                Toast.makeText(getApplication(), "补丁下载成功", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDownloadFailure(String msg) {
                Toast.makeText(getApplication(), "补丁下载失败", Toast.LENGTH_SHORT).show();

            }

            @Override
            public void onApplySuccess(String msg) {
                Toast.makeText(getApplication(), "补丁应用成功", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onApplyFailure(String msg) {
                Toast.makeText(getApplication(), "补丁应用失败", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onPatchRollback() {

            }
        };

        // 设置开发设备,默认为false,上传补丁如果下发范围指定为“开发设备”,需要调用此接口来标识开发设备
//        Bugly.setIsDevelopmentDevice(getApplication(), true);
        // 多渠道需求塞入
        // String channel = WalleChannelReader.getChannel(getApplication());
        // Bugly.setAppChannel(getApplication(), channel);
        // 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId
        // 调试时,将第三个参数改为true
        Bugly.init(getApplication(), "65663b32e8", true);  // 第二个参数就是上面提到的APPID, 第三个参数是是否开启debuglog模式
    }


    @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
        // TinkerManager.installTinker(this); 替换成下面Bugly提供的方法
        Beta.installTinker(this);
    }

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

}

然后在我们自己的application中集成TinkerApplication, 主要是第二个参数上面刚刚创建的MyApplicationLike

package com.example.administrator.buglyupdatedemo;

import com.tencent.tinker.loader.app.TinkerApplication;
import com.tencent.tinker.loader.shareutil.ShareConstants;

/**
 * Created by Administrator on 2017/10/30.
 * 自定义Application.
 *
 * 注意:这个类集成TinkerApplication类,这里面不做任何操作,所有Application的代码都会放到ApplicationLike继承类当中
*
 * 参数解析:
 * 参数1:int tinkerFlags 表示Tinker支持的类型 dex only、library only or all suuport,default: TINKER_ENABLE_ALL
 * 参数2:String delegateClassName Application代理类 这里填写你自定义的ApplicationLike
 * 参数3:String loaderClassName  Tinker的加载器,使用默认即可
 * 参数4:boolean tinkerLoadVerifyFlag  加载dex或者lib是否验证md5,默认为false
 */

public class MyApplication extends TinkerApplication{

    public MyApplication() {
        super(ShareConstants.TINKER_ENABLE_ALL, "com.example.administrator.buglyupdatedemo.MyApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader", false);
    }

}

最后把自己的application引用到我们的配置文件里, 配置下权限和bugly sdk,整个改造流程和配置就完成了




    
    
    
    
    
    


    
        
            
                

                
            
        

        
        

        

    


5、打基准包(base包)相关配置和步骤(打base包时候ef baseApkDir = "app-1103-13-41-46"这个目录值是可以任意的,用到的是打出来后的目录值)


基于tinker的bugly的集成之路和总结_第3张图片
image.png

打出来的基准包,我打的是debug包


基于tinker的bugly的集成之路和总结_第4张图片
image.png

6、接下来就是打补丁包了,这个补丁包是在patch目录下的,不是在tinkerpatch目录下的
a.先修改好你的bug(四大组件是无法更新的)
b.先将def baseApkDir = "app-1103-13-41-46"这个改为基准包目录
c.tinkerId = "patch-1.0.1"这个id改为patch-xxx, xxx对应基准包的xxx


基于tinker的bugly的集成之路和总结_第5张图片
image.png

7、最后就是在后台控制器上传补丁了


基于tinker的bugly的集成之路和总结_第6张图片
image.png

下发成功(要下载合成补丁的前提是,基准包要上报联网成功后才能去下载合成的)


基于tinker的bugly的集成之路和总结_第7张图片
image.png
image.png

坑点1:

我在试的时候踩了一个坑,下发成功后,app一直下载不了补丁包,后来发现是全量升级开启导致的,在文档里有提到,全量更新的下发的优先级是高于热更新的下发的,也就是说同时有全量更新和热更新的下发,app会优先去提示下载升级版本,如果用户一直不升级,那么补丁就一直无法下载合并的,如果用户选择升级了,在升级后会自动下载合并补丁的。


基于tinker的bugly的集成之路和总结_第8张图片
image.png

坑点二: 全量更新和热更新都是需要(冷启动)用户重新启动app才会触发的,而且热更新下载合并完补丁后还需要重新启动app才会显示修复后的状态。

你可能感兴趣的:(基于tinker的bugly的集成之路和总结)