Android热修复框架Tinker接入

Android热修复框架Tinker接入

4步接入热修复框架tinker

  1. 项目build.gradle的配置
    在dependencies添加

    classpath('com.tencent.tinker:tinker-patch-gradle-plugin:1.9.1')
  2. app目录下build.gradle的配置
    这个需要配置的地方比较多,主要参考tinker 的GitHub上的app/build.gradle

    apply plugin: 'com.android.application'
    apply plugin: 'com.tencent.tinker.patch'
    android {
        signingConfigs {
            release {
                keyAlias 'key0'
                keyPassword '123456'
                storeFile file('../keystore/skill.jks')
                storePassword '123456'
            }
        }
        compileSdkVersion 25
        buildToolsVersion '26.0.2'
        defaultConfig {
            applicationId "com.yk.skill.androidskillplatform"
            minSdkVersion 17
            targetSdkVersion 25
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            multiDexEnabled true
            buildConfigField "String", "MESSAGE", "\"I am the base apk\""
    
            buildConfigField "String", "TINKER_ID", "\"1.0\""
            buildConfigField "String", "PLATFORM", "\"all\""
        }
        dexOptions {
            jumboMode = true
        }
        buildTypes {
            release {
                minifyEnabled true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.release
            }
        }
        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'
            })
    
            annotationProcessor 'com.tencent.tinker:tinker-android-anno:1.9.1'
            provided 'com.tencent.tinker:tinker-android-anno:1.9.1'
            compile 'com.tencent.tinker:tinker-android-lib:1.9.1'
            compile 'com.android.support:multidex:1.0.1'
            compile 'io.reactivex.rxjava2:rxjava:2.1.7'
            compile 'com.squareup.retrofit2:retrofit:2.3.0'
        }
        compileOptions {
            targetCompatibility 1.8
            sourceCompatibility 1.8
        }
    }
    repositories {
        mavenCentral()
        google()
    }
    
    def gitSha() {
        try {
            String gitRev = 'git rev-parse --short HEAD'.execute(null, project.rootDir).text.trim()
            if (gitRev == null) {
                throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")
            }
            return gitRev
        } catch (Exception e) {
            throw new GradleException("can't get git rev, you should add git to system path or just input test value, such as 'testTinkerId'")
        }
    }
    
    def javaVersion = JavaVersion.VERSION_1_7
    def bakPath = file("${buildDir}/bakApk/")
    ext {
        tinkerEnabled = true
        tinkerOldApkPath = "${bakPath}/app-release-0109-10-01-47.apk"
         tinkerApplyMappingPath = "${bakPath}/app-release-0109-10-01-47-mapping.txt"
    
        tinkerApplyResourcePath = "${bakPath}/app-release-0109-10-01-47-R.txt"
        tinkerBuildFlavorDirectory = "${bakPath}/app-1018-17-32-47"
    }    
    def getOldApkPath() {
        return hasProperty("OLD_APK") ? OLD_APK : ext.tinkerOldApkPath
    }
    
    def getApplyMappingPath() {
        return hasProperty("APPLY_MAPPING") ? APPLY_MAPPING : ext.tinkerApplyMappingPath
    }
    
    def getApplyResourceMappingPath() {
        return hasProperty("APPLY_RESOURCE") ? APPLY_RESOURCE : ext.tinkerApplyResourcePath
    }
    
    def getTinkerIdValue() {
        return hasProperty("TINKER_ID") ? TINKER_ID : gitSha()
    }
    
    def buildWithTinker() {
        return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled
    }
    
    def getTinkerBuildFlavorDirectory() {
        return ext.tinkerBuildFlavorDirectory
    }
    
    if (buildWithTinker()) {
        apply plugin: 'com.tencent.tinker.patch'    
        tinkerPatch {
             oldApk = getOldApkPath()
            ignoreWarning = false
            useSign = true
            tinkerEnable = buildWithTinker()    
            buildConfig {
                applyMapping = getApplyMappingPath()
                applyResourceMapping = getApplyResourceMappingPath()
         tinkerId = getTinkerIdValue()
    keepDexApply = false
     isProtectedApp = false
     supportHotplugComponent = false
            }
    dex {
               dexMode = "jar"
    pattern = ["classes*.dex",
                           "assets/secondary-dex-?.jar"]
                loader = [
                        "tinker.sample.android.app.BaseBuildInfo"
                ]
            }
    
            lib {
                pattern = ["lib/*/*.so"]
            }
    
            res {
                pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
      ignoreChange = ["assets/sample_meta.txt"]
     largeModSize = 100
            }
     packageConfig {
             configField("patchMessage", "tinker is sample to use")
                configField("platform", "all")
                configField("patchVersion", "1.0")
            }
            sevenZip {
              zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
                 }
        }
    
        List<String> flavors = new ArrayList<>();
        project.android.productFlavors.each { flavor ->
            flavors.add(flavor.name)
        }
        boolean hasFlavors = flavors.size() > 0
        def date = new Date().format("MMdd-HH-mm-ss")
    
        android.applicationVariants.all { variant ->
            def taskName = variant.name
    
            tasks.all {
                if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {
    
                    it.doLast {
                        copy {
                            def fileNamePrefix = "${project.name}-${variant.baseName}"
                            def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}-${date}"
    
                            def destPath = hasFlavors ? file("${bakPath}/${project.name}-${date}/${variant.flavorName}") : bakPath
                            from variant.outputs.first().outputFile
                            into destPath
                            rename { String fileName ->
                                fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
                            }
    
                            from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"
                            into destPath
                            rename { String fileName ->
                                fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")
                            }
    
                            from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
                            into destPath
                            rename { String fileName ->
                                fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
                            }
                        }
                    }
                }
            }
        }
        project.afterEvaluate {
            //sample use for build all flavor for one time
            if (hasFlavors) {
                task(tinkerPatchAllFlavorRelease) {
                    group = 'tinker'
                    def originOldPath = getTinkerBuildFlavorDirectory()
                    for (String flavor : flavors) {
                        def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Release")
                        dependsOn tinkerTask
                        def preAssembleTask = tasks.getByName("process${flavor.capitalize()}ReleaseManifest")
                        preAssembleTask.doFirst {
                            String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 15)
                            project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk"
                            project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-mapping.txt"
                            project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release-R.txt"
    
                        }
    
                    }
                }
    
                task(tinkerPatchAllFlavorDebug) {
                    group = 'tinker'
                    def originOldPath = getTinkerBuildFlavorDirectory()
                    for (String flavor : flavors) {
                        def tinkerTask = tasks.getByName("tinkerPatch${flavor.capitalize()}Debug")
                        dependsOn tinkerTask
                        def preAssembleTask = tasks.getByName("process${flavor.capitalize()}DebugManifest")
                        preAssembleTask.doFirst {
                            String flavorName = preAssembleTask.name.substring(7, 8).toLowerCase() + preAssembleTask.name.substring(8, preAssembleTask.name.length() - 13)
                            project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug.apk"
                            project.tinkerPatch.buildConfig.applyMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-mapping.txt"
                            project.tinkerPatch.buildConfig.applyResourceMapping = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-debug-R.txt"
                        }
    
                    }
                }
            }
        }
    }

    这里需要注意下Android–>defaultConfig下的TINKER_ID这个有可能报tinker_id错误,我是把这里修改成一个默认的值。

    buildConfigField "String", "TINKER_ID", "\"1.0\""

    还有一个地方需要针对性的修改:这里需要填入你原始包的对应的名词,如果没有修改过build.gradle文件,则默认在app/build/bakApk目录下

    ext {
    tinkerEnabled = true
    tinkerOldApkPath = "${bakPath}/app-release-0109-10-01-47.apk"
    tinkerApplyMappingPath = "${bakPath}/app-release-0109-10-01-47-mapping.txt"
    tinkerApplyResourcePath = "${bakPath}/app-release-0109-10-01-47-R.txt"
    }
  3. 创建一个application继承DefaultApplicationLike,
package com.yk.skill.androidskillplatform;

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 com.tencent.tinker.anno.DefaultLifeCycle;
import com.tencent.tinker.lib.tinker.Tinker;
import com.tencent.tinker.lib.tinker.TinkerInstaller;
import com.tencent.tinker.loader.app.DefaultApplicationLike;
import com.tencent.tinker.loader.shareutil.ShareConstants;

@DefaultLifeCycle(application = "com.yk.skill.androidskillplatform.TinkerApplicationLike",flags = ShareConstants.TINKER_ENABLE_ALL,loadVerifyFlag = false)
public class SelfTinkerApplicationLike extends DefaultApplicationLike{

    public SelfTinkerApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }
    /**
     * install multiDex before install tinker
     * so we don't need to put the tinker lib classes in the main dex
     *
     * @param base
     */
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        MultiDex.install(base);
        TinkerInstaller.install(this);
    }

}
  1. 在需要patch的地方调用
TinkerInstaller.onReceiveUpgradePatch(context,patchPkgPath+"xxx.apk")

TinkerInstaller.onReceiveUpgradePatch(context,patchPkgPath+”xxx.apk”)

tinker更新包的生成

  1. 先生成一个基础包:点击Android-studio的右侧的gradle,然后按图示展开,最后点击assembleRelease生成基础包(基础包的位置在app/build/bakApk目录下)
    这里写图片描述
  2. 修改build.gradle中对应的名词:生成基础包后,按照图示的标记修改build.gradle中内容,必须要对应
    这里写图片描述
  3. 生成patch包:按照图示生成patch包
    这里写图片描述
  4. 将patch包放入到sd卡中对应的位置即可
    patch包的位置对应就是“TinkerInstaller.onReceiveUpgradePatch”这个方法中对应的位置,包名和路径必须匹配
TinkerInstaller.onReceiveUpgradePatch(context,patchPkgPath+"xxx.apk")

问题分析

1. 出现code=-2 则检查热更新的路径,获取检查热更新文件是否有权限
2. tinker_id出错,把tinker_id写成一个默认值

猜你喜欢

转载自blog.csdn.net/yk2fxy/article/details/79032330