只做个集成记录,不深究具体原理,后面慢慢看,按以下步骤就能完成热更新部署!

第一步:注册bugly后台

在https://bugly.qq.com/v2/申请APP获取相应的appId和key。在https://bugly.qq.com/v2/申请APP获取相应的appId和key。

第二步:引入bugly和tinker依赖

在项目中引入bugly热更新SDK:

    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.android.support:multidex:1.0.1"

在defaultConfig{}中添加:

multiDexEnabled true

在project的build.gradle添加:

classpath "com.tencent.bugly:tinker-support:latest.release"

第三步:配置tinker插件文件

新建tinker-support.gradle文件,和module的build.gradle同级:

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

def bakPath = file("${buildDir}/bakApk/")
/*在本类中需要修改:
* 1.baseApkDir
* 2-4名称的修改
* 2.baseApk
* 3.baseApkProguardMapping
* 4.baseApkResourceMapping
* 5.tinkerId  编号必须一致
* */
/**
 * 此处填写每次构建生成的基准包目录  app/build/bakApk/app-0412-18-01-33
 */
def baseApkDir = "app-0321-13-16-10"
//def myTinkerId = "base-" + rootProject.ext.android.versionName // 用于生成基准包(不用修改)
def myTinkerId = "patch-" + rootProject.ext.android.versionName + ".0.0" // 用于生成补丁包(每次生成补丁包都要修改一次,最好是 patch-${versionName}.x.x)
/**
 * 对于插件各参数的详细解析请参考
 */
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,并且必须保证唯一性 base  patch
    //tinkerId = "${myTinkerId}"  //base-1.0.3 错误版本  patch-1.0.3 补丁版本   (1.0.3基准包和补丁包版本要一致)
    tinkerId = "base-1.0.5"
    //tinkerId = "patch-1.0.4.500"
    // 构建多渠道补丁时使用
    // buildAllFlavorsDir = "${bakPath}/${baseApkDir}"

    // 是否开启反射Application模式
    enableProxyApplication = false//使用SampleApplication和SampleApplicationLike
    //enableProxyApplication = true//使用MyApplication

}

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

解释:

def bakPath = file("${buildDir}/bakApk/")
这个bakApk是你基准包存放的位置,在app/build下面。
 
def baseApkDir = "app-0321-13-16-10"
这是每次构建生成的基准包目录 app/build/bakApk/app-0321-13-16-10,每次debug或者打包都会在bakApk下生成新的包名,所以要记住每个版本对应的包名,后面才能针对不同版本的apk下发补丁包。
 
baseApk = "${bakPath}/${baseApkDir}/app-release.apk"
baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"
baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"
这三个参数需要和你app/build/bakApk/app-0321-13-16-10/app-release.apk下的apk名字对应,一定要相同
 
tinkerId = "base-1.0.5"
这个是唯一标识apk的东西,构建基准包和补丁包都要指定不同的tinkerId,并且必须保证唯一性 base  patch
1.0.5和你的版本号对应,基准包就是base-1.0.5,那么补丁包就是patch-1.0.5(一个版本都会有多个补丁包,所以一般在后面加上:patch-1.0.5.100,修改100的值,达到每次id都不一样)
 

最后别忘了在build.gradle中添加依赖插件脚本:

apply from: 'tinker-support.gradle'

第四步:自定义Application

新建一个代理Application:

/**
 * 自定义ApplicationLike类.
 *
 * 注意:这个类是Application的代理类,以前所有在Application的实现必须要全部拷贝到这里<br/>
 *
 * @author wenjiewu
 * @since 2016/11/7
 */
public class SampleApplicationLike extends DefaultApplicationLike {

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

    private  static Context context;

    private  Application application;

    public static Context getContext() {
        return context;
    }

    public SampleApplicationLike(Application application, int tinkerFlags,
                                 boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime,
                                 long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime,
                applicationStartMillisTime, tinkerResultIntent);
        this.application=application;
    }


    @Override
    public void onCreate() {
        super.onCreate();
        context = application.getApplicationContext();
        MobSDK.init(context);
        OkGo.getInstance().init(application);
        JPushInterface.setDebugMode(true); 	// 设置开启日志,发布时请关闭日志
        JPushInterface.init(context);
        // 设置是否开启热更新能力,默认为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
        Bugly.init(getApplication(), "1234567890", false);
    }


    @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);

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

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

    @Override
    public void onTerminate() {
        super.onTerminate();
        Beta.unInit();
    }
}

你的bugly后台上的appid:

 Bugly.init(getApplication(), "1234567890", false);

如果你自定义了application需要把原来所有在自定义里面实现的功能搬到这个类里面来,这只是个代理类,继续新建一个继承TinkerApplication的类:

/**
 * 自定义Application.
 *
 * 注意:这个类集成TinkerApplication类,这里面不做任何操作,所有Application的代码都会放到ApplicationLike继承类当中<br/>
 * <pre>
 * 参数解析:
 * 参数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
 * </pre>
 * @author wenjiewu
 * @since 2016/11/15
 */
public class SampleApplication extends TinkerApplication {
    public SampleApplication() {
        super(ShareConstants.TINKER_ENABLE_ALL, "你的包名.SampleApplicationLike",
                "com.tencent.tinker.loader.TinkerLoader", false);
    }
}

在AndroidManifest中修改为:

android:name="你的包名.SampleApplication"

以上这种方式是在enableProxyApplication = false的情况下,在tinker-support.gradle中我们有定义其为false或者true的时候:

   // 是否开启反射Application模式
    enableProxyApplication = false//使用SampleApplication和SampleApplicationLike
    //enableProxyApplication = true//使用MyApplication

一般我们都是false情况下,使用true反射的方式不稳定。

第五步:打包

需要使用gradle打包(项目右侧gradle),正常基准包打包如下:

使用bugly+tinker热更新

补丁包打包如下:

使用bugly+tinker热更新

打包完后APK存放在工程目录的位置:

使用bugly+tinker热更新

注意:

bugly后台我只用来热更新,版本迭代是通过自己的服务器,放自己服务器上的APK一定是通过gradle打包在bakApk下面生成的apk,不然下发补丁包时没用(当时忘了,直接签名打包导致热更新的时候没用);

基准包是base+版本号,补丁包是patch+版本号+数字且每次都需修改;

baseApkDir只有在版本变动的时候才需修改为对应的版本的名称再进行打补丁包;

在app的build.gradle中的buildTypes{}里面未加signingConfig signingConfigs.release,每次打包都没签名,上传bugly管理后台时老是报错。所以正确应该是如下:

signingConfigs {

        def alias = '别名'
        def password = '密码'
        def filePath = 'D:/Yang/Android/project/key/android.keystore'
        debug {
            keyAlias alias
            keyPassword password
            storeFile file(filePath)
            storePassword(password)
        }
        release {

            keyAlias alias
            keyPassword password
            storeFile file(filePath)
            storePassword(password)
        }
    }

buildTypes {
        release {
            minifyEnabled false
//            shrinkResources true
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            proguardFile 'D:/Yang/Android/project/key/android.keystore'
        }
    }

 

第六步:使用bugly下发补丁包

下发补丁包的过程根据步骤来就行了。

需要提一下基准包需要联网打开一次才能生效,补丁包下载成功后需重启应用方能生效。

Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大组件。

 

 

       以上只是自己工程使用bugly+tinker的心得,有些地方可能不适合你工程,tinker的使用也没说完,就把自己用到的在这里做个记录,因为太健忘了。

 

 

 

 

 

 

 

 

 

 

相关文章: