美文网首页
bugly+tinker+walle接入整合

bugly+tinker+walle接入整合

作者: 明日复日明 | 来源:发表于2017-12-18 11:19 被阅读0次

    集成 Bugly 热更新的详细操作可以参考官网的文档:

    https://bugly.qq.com/docs/user-guide/instruction-manual-android-hotfix/?v=20171109131920

    第一步:添加插件依赖

    工程根目录下“build.gradle”文件中添加:

    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            // tinkersupport插件, 其中lastest.release指拉取最新版本,也可以指定明确版本号,例如1.0.4
            classpath "com.tencent.bugly:tinker-support:1.1.1"
        }
    }
    

    第二步:集成SDK

    在app module的“build.gradle”文件中添加(示例配置):

    android {
            defaultConfig {
              ndk {
                //设置支持的SO库架构
                abiFilters 'armeabi' //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
              }
              sourceSets {
                main {
                    jniLibs.srcDirs = ['libs']
                }
              }
            }
          }
          dependencies {
              compile "com.android.support:multidex:1.0.1" // 多dex配置
              compile 'com.tencent.bugly:crashreport_upgrade:1.3.4'
              compile 'com.tencent.bugly:nativecrashreport:latest.release'
          }
    

    在app module的“build.gradle”文件中添加:

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

    tinker-support.gradle内容如下所示(示例配置):

    注:您需要在同级目录下创建tinker-support.gradle这个文件哦。

    apply plugin: 'com.tencent.bugly.tinker-support'
    
    def bakPath = file("${buildDir}/bakApk/")
    
    /**
     * 此处填写每次构建生成的基准包目录
     */
    def baseApkDir = "app-1215-15-15-51"
    
    /**
     * 对于插件各参数的详细解析请参考
     */
    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 = "${verName()}-${buildTime()}" //版本名与时间戳结合,确保了唯一性
    
        // 构建多渠道补丁时使用
        // buildAllFlavorsDir = "${bakPath}/${baseApkDir}"
    
        // 是否启用加固模式,默认为false.(tinker-spport 1.0.7起支持)
        // isProtectedApp = true
    
        // 是否开启反射Application模式
        enableProxyApplication = false
    
        supportHotplugComponent = true
    }
    
    //获取版本名
    def verName() {
        def versionPropsFile = file("../version.properties")
        if (versionPropsFile.canRead()) {
            Properties versionProps = new Properties()
            versionProps.load(new FileInputStream(versionPropsFile))
            return versionProps['VERSION_NAME']
        } else {
            throw new GradleException("Could not read gradle.properties")
        }
    }
    
    //获取构建时间
    def buildTime() {
        return new Date().format("MMdd-HH-mm-ss", TimeZone.getTimeZone("GMT+8"))
    }
    
    /**
     * 一般来说,我们无需对下面的参数做任何的修改
     * 对于各参数的详细介绍请参考:
     * 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的分配
        }
    }
    

    由于tinkerId动态获取版本名使用到了version.properties,因此需要根目录下创建version.properties,内容如下:

    VERSION_NAME=2.0
    VERSION_CODE=2
    

    既然将 versionCode 和 versionName 配置在 properties 文件中,那么 app module 的 gradle 文件中,defaultConfig{} 中指定版本名和版本号直接使用该配置

    defaultConfig {
            ...
            versionCode verCode()
            versionName verName()
            ...
        }
    

    verCode()与verName()代码如下:

    //获取版本号
    def verCode() {
        def versionPropsFile = file("../version.properties")
        if (versionPropsFile.canRead()) {
            Properties versionProps = new Properties()
            versionProps.load(new FileInputStream(versionPropsFile))
            def int verCode = versionProps['VERSION_CODE'].toInteger()
            return verCode;
        } else {
            throw new GradleException("Could not read gradle.properties")
        }
    }
    
    //获取版本名
    def verName() {
        def versionPropsFile = file("../version.properties")
        if (versionPropsFile.canRead()) {
            Properties versionProps = new Properties()
            versionProps.load(new FileInputStream(versionPropsFile))
            def String verName = versionProps['VERSION_NAME'].toString()
            return verName;
        } else {
            throw new GradleException("Could not read gradle.properties")
        }
    }
    

    第三步:初始化SDK

    enableProxyApplication = false 的情况

    这是Tinker推荐的接入方式,一定程度上会增加接入成本,但具有更好的兼容性。
    自定义Application:

    /*注意:这个类集成TinkerApplication类,这里面不做任何操作,所有Application的代码都会放到ApplicationLike继承类当中
    参数解析
    参数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 SampleApplication extends TinkerApplication {
        public SampleApplication() {
            super(ShareConstants.TINKER_ENABLE_ALL, "xxx.xxx.SampleApplicationLike",
                    "com.tencent.tinker.loader.TinkerLoader", false);
        }
    }
    

    自定义SampleApplicationLike:

    public class SampleApplicationLike extends DefaultApplicationLike {
    
        public static final String TAG = "Tinker.SampleApplicationLike";
    
        public SampleApplicationLike(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();
            // 按渠道维度来统计你们app的Crash数据
            String channel = WalleChannelReader.getChannel(getApplication());
            Bugly.setAppChannel(getApplication(), channel);
            // 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId
            // 调试时,将第三个参数改为true
            Bugly.init(getApplication(), "0f8e719879", true);
            Utils.init(getApplication());
        }
    
    
        @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);
            Beta.canNotifyUserRestart = true; // 是否弹出提示用户重启应用的对话框
        }
    
        @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
        public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
            getApplication().registerActivityLifecycleCallbacks(callbacks);
        }
    }
    

    SampleApplicationLike这个类是Application的代理类,以前所有在Application的实现必须要全部拷贝到这里,在onCreate方法调用SDK的初始化方法,在onBaseContextAttached中调用Beta.installTinker(this);
    enableProxyApplication = true 的情况

    public class MyApplication extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
            // 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId
            // 调试时,将第三个参数改为true
            Bugly.init(this, "900029763", false);
        }
    
        @Override
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(base);
            // you must install multiDex whatever tinker is installed!
            MultiDex.install(base);
    
            // 安装tinker
            Beta.installTinker();
        }
    }
    

    第四步:AndroidManifest.xml配置

    1. 权限配置

    <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" />
    

    2.Activity的配置

    <activity
        android:name="com.tencent.bugly.beta.ui.BetaActivity"
        android:configChanges="keyboardHidden|orientation|screenSize|locale"
        android:theme="@android:style/Theme.Translucent" />
    

    3. 配置FileProvider
    注意:如果您想兼容Android N或者以上的设备,必须要在AndroidManifest.xml文件中配置FileProvider来访问共享路径的文件。

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.fileProvider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>
    

    如果你使用的第三方库也配置了同样的FileProvider, 可以通过继承FileProvider类来解决合并冲突的问题,示例如下:

    <provider
        android:name=".utils.BuglyFileProvider"
        android:authorities="${applicationId}.fileProvider"
        android:exported="false"
        android:grantUriPermissions="true"
        tools:replace="name,authorities,exported,grantUriPermissions">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"
            tools:replace="name,resource"/>
    </provider>
    

    这里要注意一下,FileProvider类是在support-v4包中的,检查你的工程是否引入该类库。
    在res目录新建xml文件夹,创建provider_paths.xml文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <!-- /storage/emulated/0/Download/${applicationId}/.beta/apk-->
        <external-path name="beta_external_path" path="Download/"/>
        <!--/storage/emulated/0/Android/data/${applicationId}/files/apk/-->
        <external-path name="beta_external_files_path" path="Android/data/"/>
    </paths>
    

    第五步:混淆配置

    -dontwarn com.tencent.bugly.**
    -keep public class com.tencent.bugly.** { *; }
    -keep class android.support.** { *; }
    

    以上就是接入的流程。接下来是如何使用:

    • 打基准包安装并上报联网(注:填写唯一的tinkerId)
    • 对基准包的bug修复(可以是Java代码变更,资源的变更)
    • 修改基准包路径、修改补丁包tinkerId、mapping文件路径(如果开启了混淆需要配置)、resId文件路径
    • 执行buildTinkerPatchRelease打Release版本补丁包
    • 选择app/build/outputs/patch目录下的补丁包并上传(注:不要选择-
    • tinkerPatch目录下的补丁包,不然上传会有问题)
    • 编辑下发补丁规则,点击立即下发
    • 杀死进程并重启基准包,请求补丁策略(SDK会自动下载补丁并合成)
    • 再次重启基准包,检验补丁应用结果
    • 查看页面,查看激活数据的变化

    普通打包

    1.执行assembleRelease编译生成基准包:

    image.png
    这个会在build/outputs/bakApk路径下生成每次编译的基准包、混淆配置文件、资源Id文件,如下图所示:
    image.png
    实际应用中,请注意保存线上发布版本的基准apk包、mapping文件、R.txt文件,如果线上版本有bug,就可以借助我们tinker-support插件进行补丁包的生成。
    

    2.根据基线版本生成补丁包

    image.png
    执行构建补丁包的task
    image.png
    生成的补丁包在build/outputs/patch目录下:
    image.png
    3.上传补丁包到平台 image.png
    image.png
    image.png
    定义开发设备需要执行
    Bugly.setIsDevelopmentDevice(getApplicationContext(), true);

    下发后耐心等候即可,重启重启再重启,然后就可以了~~

    多渠道打包工具打多渠道包方式

    使用walle来打多渠道包,新一代多渠道打包神器。

    工程根目录下“build.gradle”文件中添加:

    buildscript {
        dependencies {
            ...
            classpath 'com.meituan.android.walle:plugin:1.1.3'
            ...
        }
    }
    

    在app moudle的"build.gradle"文件中添加:

    // 多渠道使用walle示例(注:多渠道使用)
    apply from: 'multiple-channel.gradle'
    
    dependencies {
        ...
        compile 'com.meituan.android.walle:library:1.1.3'
        ...
    }
    

    multiple-channel.gradle内容如下:

    注:您需要在同级目录下创建multiple-channel.gradle这个文件哦。

    apply plugin: 'walle'
    walle {
        // 指定渠道包的输出路径
        apkOutputFolder = new File("${project.buildDir}/outputs/channels");
        // 定制渠道包的APK的文件名称
        apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk';
        // 渠道配置文件
        channelFile = new File("${project.getProjectDir()}/channel")
    }
    

    配置项具体解释:

    • apkOutputFolder:指定渠道包的输出路径, 默认值为new File("${project.buildDir}/outputs/apk")
    • apkFileNameFormat:定制渠道包的APK的文件名称, 默认值为'${appName}-${buildType}-${channel}.apk'
      可使用以下变量:
        projectName - 项目名字
        appName - App模块名字
        packageName - applicationId (App包名packageName)
        buildType - buildType (release/debug等)
        channel - channel名称 (对应渠道打包中的渠道名字)
        versionName - versionName (显示用的版本号)
        versionCode - versionCode (内部版本号)
        buildTime - buildTime (编译构建日期时间)
        fileSHA1 - fileSHA1 (最终APK文件的SHA1哈希值)
        flavorName - 编译构建 productFlavors 名
    
    • channelFile:包含渠道配置信息的文件路径。

    与multiple-channel.gradle同级目录创建channel配置:

    meituan # 美团
    samsungapps # 三星
    hiapk
    anzhi
    xiaomi # 小米
    91com
    gfan
    appchina
    nduoa
    3gcn
    mumayi
    10086com
    wostore
    189store
    lenovomm
    hicloud
    meizu
    wandou
    # Google Play
    # gooleplay
    # 百度
    baidu
    #
    # 360
    360cn
    #
    # 应用宝
    myapp
    

    执行task


    image.png

    会得到:


    image.png

    在代码中获取渠道信息:
    String channel = WalleChannelReader.getChannel(this.getApplicationContext());
    如果你已经集成了Bugly的异常上报,你就可以通过以下方式来塞入渠道信息:

    // 按渠道维度来统计你们app的Crash数据
            String channel = WalleChannelReader.getChannel(getApplication());
            Bugly.setAppChannel(getApplication(), channel);
            // 这里实现SDK初始化,appId替换成你的在Bugly平台申请的appId
            // 调试时,将第三个参数改为true
            Bugly.init(getApplication(), "0f8e719879", true);
    

    在打渠道包的过程,因为会走编译流程,热更新插件也会在bakApk生成对应的基线版本,这个跟普通打包就没有差别了,只需要上传补丁包到补丁管理后台,然后下发即可。

    附上信息来源:

    相关文章

      网友评论

          本文标题:bugly+tinker+walle接入整合

          本文链接:https://www.haomeiwen.com/subject/xippwxtx.html