Android 腾讯Bugly的应用升级&热更新

Posted BandaYung

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 腾讯Bugly的应用升级&热更新相关的知识,希望对你有一定的参考价值。

经过去年的九月份至现在,发现自己很久没有写过比较好的文章了。今天就趁着通宵的劲,写一下对腾讯Bugly的应用升级&热更新的理解,希望对新手有所帮助,有兴趣的可以了解下,没兴趣的也可以看完之后吐槽我。。。

Bugly 文档中心:https://bugly.qq.com/docs/

官方文档写的还是蛮详细的,不过有些地方还是自己绕了很多弯,请教了公司大神才弄清楚的。

*本篇文章已授权 玩android 独家发布

项目配置的话,本人选择直接配置应用升级&热更新。

一、配置gradle跟multiDexKeep

1、project的配置:build.gradle:

buildscript 
    repositories 
        google()
        jcenter()
    
    dependencies 
        classpath 'com.android.tools.build:gradle:3.1.3'
        
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files

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


allprojects 
    repositories 
        google()
        jcenter()
    


task clean(type: Delete) 
    delete rootProject.buildDir


2、module的配置:build.gradle

apply plugin: 'com.android.application'
// 热更新依赖插件脚本,如果不想要热更新可以注释掉
apply from: 'tinker-support.gradle'
//全局配置
apply from: "../config.gradle"

android 
    compileSdkVersion compile_sdk_version
    buildToolsVersion build_tools_version
    defaultConfig 
        applicationId "com.example.administrator.myapplication"
        minSdkVersion min_sdk_version
        targetSdkVersion target_sdk_version
        versionCode version_code
        versionName version_name
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        multiDexEnabled true
        multiDexKeepProguard file('multiDexKeep.pro')
        ndk 
            // 设置支持的SO库架构
            abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'arm64-v8a' //, 'x86_64'
        
        javaCompileOptions 
            annotationProcessorOptions 
                includeCompileClasspath true
            
        
    

    signingConfigs 
        debug 
            keyAlias 'debug'
            keyPassword '123456'
            storeFile file('../debug.jks')
            storePassword '123456'
            v1SigningEnabled true
            v2SigningEnabled false
        

        release 
            v1SigningEnabled true
            v2SigningEnabled false
            keyAlias 'key0'
            keyPassword '123456'
            storeFile file('../release.jks')
            storePassword '123456'
        
    


    buildTypes 
        release 
            debuggable log_debug
            shrinkResources false
            minifyEnabled false
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
            buildConfigField("boolean", "LOG_DEBUG", String.valueOf(log_debug))
        
        debug 
            debuggable true
            jniDebuggable true
            signingConfig signingConfigs.debug
            minifyEnabled false
            zipAlignEnabled false
            buildConfigField("boolean", "LOG_DEBUG", String.valueOf(log_debug))
        
    

    dexOptions 
//        incremental true
//        maxProcessCount 2
        javaMaxHeapSize "4g"
        jumboMode = true
    

    lintOptions 
        checkReleaseBuilds false
        abortOnError false
    

    sourceSets 
        main 
            jniLibs.srcDirs = ['libs']
        
    

    repositories 
        flatDir 
            dirs 'libs'
        
    


dependencies 
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support.constraint:constraint-layout:1.1.2'
    testCompile 'junit:junit:4.12'
    androidTestCompile 'com.android.support.test:runner:1.0.2'
    androidTestCompile "com.android.support:support-annotations:$support_library_version"
    compile "com.android.support:appcompat-v7:$support_library_version"
    compile "com.android.support:support-v4:$support_library_version"
    compile "com.android.support:multidex:1.0.3" // 多dex配置
    //------------------Bugly---------------------------------------------------------
    //    compile 'com.tencent.bugly:crashreport_upgrade:latest.release'
    //    compile 'com.tencent.bugly:nativecrashreport:latest.release'
    compile 'com.tencent.bugly:crashreport_upgrade:1.3.4'
    //升级SDK已经集成crash上报功能
    compile 'com.tencent.bugly:nativecrashreport:3.3.1'
    //------------------Bugly---------------------------------------------------------

3、热更新配置:tinker-support.gradle

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

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

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

/**
 * 此处填写每次构建生成的基准包目录
 */
def baseApkDir = "app-0710-15-48-49"

/**
 * 对于插件各参数的详细解析请参考
 */
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"
//    baseApk = "$bakPath/$baseApkDir/app-debug.apk"

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

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

    // 构建基准包和补丁包都要指定不同的tinkerId,并且必须保证唯一性
    // **基准包这里最好是改成base-versionname,patch包就改成patch-versionname,每个版本的都不一样**
    tinkerId = "patch-1.0.1"

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

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

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



/**
 * 一般来说,我们无需对下面的参数做任何的修改
 * 对于各参数的详细介绍请参考:
 * 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的分配
    

基准包一般都是以app-release.apk正式版,如果想要用debug版的话,上面的baseApk、baseApkProguardMapping、baseApkResourceMapping切换下。

4、全部配置:config.gradle

ext.target_sdk_version = 27
ext.min_sdk_version = 19
ext.compile_sdk_version = 27
ext.build_tools_version = '27.0.3'
ext.support_library_version = '27.1.1'

//TODO 调试模式的开启关闭
ext.log_debug = false
ext.version_code = 1
ext.version_name = "1.0.1"

5、Bugly MultiDex注意事项,把Bugly的类放到主Dex,即写在multiDexKeep.pro

-keep class com.tencent.bugly.** *;
-keep class com.example.administrator.myapplication.AppLike*;

这里你的AppLike放在那里,对应修改其路径

附上项目目录结构图,在说明相对应的配置

二、XML的配置:

1、权限跟activity和provider:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.administrator.myapplication">

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

    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

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

        <!-- 用于兼容7.0以上设备 -->
        <provider
            android:name=".MyFileProvider"
            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>

    </application>

</manifest>

2、在res目录新建xml文件夹,创建provider_paths.xml文件如下:

<?xml version="1.0" encoding="utf-8"?>
<paths>

    <!--Bugly:这里配置的两个外部存储路径是升级SDK下载的文件可能存在的路径,一定要按照上面格式配置,不然可能会出现错误。-->
    <!-- /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/" />

    <root-path
        name="root"
        path="root/" />
    <!--代表设备的根目录new File("/");-->
    <files-path
        name="my_image"
        path="images/" />
    <!--代表context.getFilesDir()-->
    <cache-path
        name="cache"
        path="caches/" />
    <!--代表context.getCacheDir()-->
    <external-path
        name="my_dir"
        path="dirs/" />
    <!--代表Environment.getExternalStorageDirectory()-->
    <!--<external-files-path/>-->
    <!--代表context.getExternalFilesDirs()-->
    <!--<external-cache-path/>-->
    <!--代表getExternalCacheDirs()-->
</paths>

三、代码模块

1、App.java

public class App extends TinkerApplication 
    public static App mInstance;

    public App() 
	    //同样修改成自己AppLike的路径
        super(ShareConstants.TINKER_ENABLE_ALL, "com.example.administrator.myapplication.AppLike",
                "com.tencent.tinker.loader.TinkerLoader", false);
    

    public static App getInstance() 
        return mInstance;
    

    @Override
    public void onCreate() 
        super.onCreate();
        mInstance = this;
    

2、AppLike.java

public class AppLike extends DefaultApplicationLike 

    public static final String TAG = "Tinker.AppLike";

    public AppLike(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();
        //设置自定义升级对话框UI布局
        Beta.upgradeDialogLayoutId = R.layout.upgrade_dialog;
        //添加可显示弹窗的Activity
        Beta.canShowUpgradeActs.add(MainActivity.class);
        //TODO 初始化,AppID替换成平台上项目中产品信息的AppID
		Bugly.init(getApplication(), "AppID", BuildConfig.LOG_DEBUG);
        //  Q:你们是怎么定义开发设备的?
        //  BitmapQrCodeDecoder:我们会提供接口Bugly.setIsDevelopmentDevice(getApplicationContext(), true);,
        //  我们后台就会将你当前设备识别为开发设备,如果设置为false则非开发设备,我们会根据这个配置进行策略控制。
        Bugly.setIsDevelopmentDevice(getApplication(), BuildConfig.LOG_DEBUG);
    

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) 
        super.onBaseContextAttached(base);
        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);
    


3、这里要注意一下,AndroidManifest.xml中的FileProvider类是在support-v4包中的,检查你的工程是否引入该类库。

项目中新建一个MyFileProvider.java

public class MyFileProvider extends FileProvider 


四、打包及发布的流程(重中之重)

1、如果你还不知道怎么打包的话,可以看下图。如果知道的话可以看下如何打补丁包,双击一下等待数秒就打包好了。

2、打完包的apk跟补丁包文件目录

bakApk中app-**目录下的apk为基准包(可以用于官网中全量更新:发布新版本)
patch目录下的patch_signed_7zip.apk为补丁包(可以用于官网热更新:发布新补丁)

3、打包需要修改配置如下

  • config.gradle

ext.log_debug = false
ext.version_code = 1
ext.version_name = “1.0.1”

  • tinker-support.gradle
    tinkerId可以不填,不过要配置自动生成tinkerId(autoGenerateTinkerId=true)

def baseApkDir = “app-0710-18-39-06”
tinkerId = “patch-1.0.1”

项目版本打包,首先要确定如下配置

ext.log_debug = false //调试模式的开启关闭,正式包要改成false
ext.version_code = 1
ext.version_name = “1.0.1”
tinkerId = “patch-1.0.1” // tinkerId=patch-version_name

点击上图中的assembleRelease进行打正式包,生成app-0710-18-39-06下的app-release.apk(这个就是正式包,也算是基准包),然后把apk上传到bugly,如下图

图中第三步,可以修改更新提示框,例如:AppLike.class中的Beta.upgradeDialogLayoutId = R.layout.upgrade_dialog;
只需在项目中新建一个布局,不过那些按钮等都要设置对应的tag,才会起作用,具体看Bugly Android 应用升级 SDK 高级配置

特别要注意的是:打包成功之后,最好把app-0710-18-39-06文件夹保存起来。为什么?

比如现在版本上线,突然出现一个重大bug,这时候可以选择用热更新进行修复。你也不用重新多弄个版本,用户也不用去重新下载apk。

然后项目打补丁包,需要如下配置

def baseApkDir = “app-0710-18-39-06”

这里的app-0710-18-39-06就是刚刚说要保存的基准包对应的文件名

弄完之后就上传补丁包,如下图

一般确认无误的情况,下发范围都是选择全部设备。

如果你想选择开发设备进行补丁包测试的话,那么又要修改如下配置,首先修改 ext.log_debug = true。

打包app-0710-18-39-06(包1):ext.log_debug = false(作为全部设备使用)
打包app-0710-18-40-40(包2):ext.log_debug = true(作为开发设备使用)

这两个app-××× 都是相继打包出来的,也就是上面说的保存包1的时候,顺便改为true打包出包2,两个都一起保存起来,然后才有以下补丁包的说法。

具体流程如如下,画得有点丑,凑合着看呗(宝图哦)


最后经过多轮测试,发现打补丁时,baseApkDir必须是认准基准包的。而tinkerid可以不用是patch-versionname,但必须不能与之前的tinkerid重复。


文章涉及到的Demo,已上传至github


项目遇到几个坑如下:

一、这里遇到一个坑,就是发布app之后,被“强制停止了”,然后重启提示“启动策略失败”。

原来是下发上限跟激活上限的问题,修改成100w就ok了

二、AS更新到3.4(当前用的版本)后找不到build下找不到assembleRelease,其实移动到了other->assembleRelease

三、点击更新老是提示“检查版本更新失败”

调用Beta.checkUpgrade()就有这样的提示,原来是android bugly不支持Android P,官方文档上最高是适配到8.x。

Android 9.0上会报联网失败,需要在项目中的res->xml文件中新建network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">android.bugly.qq.com</domain>
    </domain-config>
</network-security-config>

四、bugly热更新已激活,但是没起作用

目前了解好像是部分手机不支持,建议换个手机试试,实在不行可以弄个模拟器跑跑看


以上都是本人的一些见解,如有误点请帮忙指正,谢谢!!

看到这里的话,累了吧,分享个小姐姐给你认识

以上是关于Android 腾讯Bugly的应用升级&热更新的主要内容,如果未能解决你的问题,请参考以下文章

竞品调研 - 腾讯bugly&fabric

腾讯bugly干货分享微信Android热补丁实践演进之路

Android 内存优化总结&实践

腾讯bugly干货分享Android自绘动画实现与优化实战——以Tencent OS录音机波形动

Android 腾讯Bugly——异常上报和应用更新

腾讯Bugly干货分享Android 插件技术实战总结