Tinker集成流程

本文介绍了腾讯Tinker开源项目的集成与升级方法。包括添加插件、在gradle.properties添加版本号、新建tinker - support.gradle文件并插入内容、在build.gradle新增内容、新建MyApplicationLike、改造application,最后提及了升级补丁的操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开源项目链接:腾讯tinker

1.添加插件

classpath ("com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}") { changing = true }

2.在gradle.properties里添加版本号

TINKER_VERSION=1.9.13

3.在集成app目录下新建tinker-support.gradle文件,插入文件内容

apply plugin: 'com.tencent.tinker.patch'

def bakPath = file("${buildDir}/../bakApk/")
def baseInfo = "hexamination-0617-18-30-55"
def variantName = "release"
def flavorname="hexaminationRelease"
def versionCode = 5
def versionName = "0.0.18"
def tinkerIdname= versionName
def PLATFORM = "all"
def pathPrefix = "${bakPath}/${baseInfo}/${flavorname}/"
def name = "${flavorname}_${versionCode}_${versionName}"
def tinkerBuildFlavorDirectory = "${bakPath}/${baseInfo}"
tinkerPatch {
    /**
     * necessary,default 'null'
     * the old apk path, use to diff with the new apk to build
     * add apk from the build/bakApk
     */
    oldApk =  "${pathPrefix}/${name}.apk"
    /**
     * optional,default 'false'
     * there are some cases we may get some warnings
     * if ignoreWarning is true, we would just assert the patch process
     * case 1: minSdkVersion is below 14, but you are using dexMode with raw.
     *         it must be crash when load.
     * case 2: newly added Android Component in AndroidManifest.xml,
     *         it must be crash when load.
     * case 3: loader classes in dex.loader{} are not keep in the main dex,
     *         it must be let tinker not work.
     * case 4: loader classes in dex.loader{} changes,
     *         loader classes is ues to load patch dex. it is useless to change them.
     *         it won't crash, but these changes can't effect. you may ignore it
     * case 5: resources.arsc has changed, but we don't use applyResourceMapping to build
     */
    ignoreWarning = true

    /**
     * optional,default 'true'
     * whether sign the patch file
     * if not, you must do yourself. otherwise it can't check success during the patch loading
     * we will use the sign config with your build type
     */
    useSign = true

    /**
     * optional,default 'true'
     * whether use tinker to build
     */
    tinkerEnable = true

    /**
     * Warning, applyMapping will affect the normal android build!
     */
    buildConfig {
        /**
         * optional,default 'null'
         * if we use tinkerPatch to build the patch apk, you'd better to apply the old
         * apk mapping file if minifyEnabled is enable!
         * Warning:
         * you must be careful that it will affect the normal assemble build!
         */
        applyMapping =  "${pathPrefix}/${name}-mapping.txt"
        /**
         * optional,default 'null'
         * It is nice to keep the resource id from R.txt file to reduce java changes
         */
        applyResourceMapping = "${pathPrefix}/${name}-R.txt"

        /**
         * necessary,default 'null'
         * because we don't want to check the base apk with md5 in the runtime(it is slow)
         * tinkerId is use to identify the unique base apk when the patch is tried to apply.
         * we can use git rev, svn rev or simply versionCode.
         * we will gen the tinkerId in your manifest automatic
         *
         */
        tinkerId = tinkerIdname

        /**
         * if keepDexApply is true, class in which dex refer to the old apk.
         * open this can reduce the dex diff file size.
         */
        keepDexApply = false

        /**
         * optional, default 'false'
         * Whether tinker should treat the base apk as the one being protected by app
         * protection tools.
         * If this attribute is true, the generated patch package will contain a
         * dex including all changed classes instead of any dexdiff patch-info files.
         */
        isProtectedApp = false

        /**
         * optional, default 'false'
         * Whether tinker should support component hotplug (add new component dynamically).
         * If this attribute is true, the component added in new apk will be available after
         * patch is successfully loaded. Otherwise an error would be announced when generating patch
         * on compile-time.
         *
         * <b>Notice that currently this feature is incubating and only support NON-EXPORTED Activity</b>
         */
        supportHotplugComponent = false
    }

    dex {
        /**
         * optional,default 'jar'
         * only can be 'raw' or 'jar'. for raw, we would keep its original format
         * for jar, we would repack dexes with zip format.
         * if you want to support below 14, you must use jar
         * or you want to save rom or check quicker, you can use raw mode also
         */
        dexMode = "jar"

        /**
         * necessary,default '[]'
         * what dexes in apk are expected to deal with tinkerPatch
         * it support * or ? pattern.
         */
        pattern = ["classes*.dex",
                   "assets/secondary-dex-?.jar"]
        /**
         * necessary,default '[]'
         * Warning, it is very very important, loader classes can't change with patch.
         * thus, they will be removed from patch dexes.
         * you must put the following class into main dex.
         * Simply, you should add your own application {@code tinker.sample.android.SampleApplication}
         * own tinkerLoader, and the classes you use in them
         *
         */
        loader = [
                //use sample, let BaseBuildInfo unchangeable with tinker
                "tinker.sample.android.app.BaseBuildInfo"
        ]
    }

    lib {
        /**
         * optional,default '[]'
         * what library in apk are expected to deal with tinkerPatch
         * it support * or ? pattern.
         * for library in assets, we would just recover them in the patch directory
         * you can get them in TinkerLoadResult with Tinker
         */
        pattern = ["lib/*/*.so"]
    }

    res {
        /**
         * optional,default '[]'
         * what resource in apk are expected to deal with tinkerPatch
         * it support * or ? pattern.
         * you must include all your resources in apk here,
         * otherwise, they won't repack in the new apk resources.
         */
        pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]

        /**
         * optional,default '[]'
         * the resource file exclude patterns, ignore add, delete or modify resource change
         * it support * or ? pattern.
         * Warning, we can only use for files no relative with resources.arsc
         */
        ignoreChange = ["assets/sample_meta.txt"]

        /**
         * default 100kb
         * for modify resource, if it is larger than 'largeModSize'
         * we would like to use bsdiff algorithm to reduce patch file size
         */
        largeModSize = 100
    }

    packageConfig {
        /**
         * optional,default 'TINKER_ID, TINKER_ID_VALUE' 'NEW_TINKER_ID, NEW_TINKER_ID_VALUE'
         * package meta file gen. path is assets/package_meta.txt in patch file
         * you can use securityCheck.getPackageProperties() in your ownPackageCheck method
         * or TinkerLoadResult.getPackageConfigByName
         * we will get the TINKER_ID from the old apk manifest for you automatic,
         * other config files (such as patchMessage below)is not necessary
         */
        configField("patchMessage", "tinker is sample to use")
        /**
         * just a sample case, you can use such as sdkVersion, brand, channel...
         * you can parse it in the SamplePatchListener.
         * Then you can use patch conditional!
         */
        configField("platform", "all")
        /**
         * patch version via packageConfig
         */
        configField("patchVersion", "1.0")
    }
    //or you can add config filed outside, or get meta value from old apk
    //project.tinkerPatch.packageConfig.configField("test1", project.tinkerPatch.packageConfig.getMetaDataFromOldApk("Test"))
    //project.tinkerPatch.packageConfig.configField("test2", "sample")

    /**
     * if you don't use zipArtifact or path, we just use 7za to try
     */
    sevenZip {
        /**
         * optional,default '7za'
         * the 7zip artifact path, it will use the right 7za with your platform
         */
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
        /**
         * optional,default '7za'
         * you can specify the 7za path yourself, it will overwrite the zipArtifact value
         */
//        path = "/usr/local/bin/7za"
    }
}

/**
 * 用于用户在代码中判断tinkerPatch是否被使能
 */
android {
    defaultConfig {
        buildConfigField "String", "TINKER_ID", "\"${tinkerIdname}\""
        buildConfigField "String", "PLATFORM", "\"${PLATFORM}\""
    }
}

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

/**
 * bak apk and mapping
 */
android.applicationVariants.all { variant ->
    /**
     * task type, you want to bak
     */
    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

                    if (variant.metaClass.hasProperty(variant, 'packageApplicationProvider')) {
                        def packageAndroidArtifact = variant.packageApplicationProvider.get()
                        if (packageAndroidArtifact != null) {
                            from new File(packageAndroidArtifact.outputDirectory, variant.outputs.first().apkData.outputFileName)
                        } else {
                            from variant.outputs.first().mainOutputFile.outputFile
                        }
                    } else {
                        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 = tinkerBuildFlavorDirectory
            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 = tinkerBuildFlavorDirectory
            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"
                }

            }
        }
    }
}



task sortPublicTxt() {
    doLast {
        File originalFile = project.file("public.txt")
        File sortedFile = project.file("public_sort.txt")
        List<String> sortedLines = new ArrayList<>()
        originalFile.eachLine {
            sortedLines.add(it)
        }
        Collections.sort(sortedLines)
        sortedFile.delete()
        sortedLines.each {
            sortedFile.append("${it}\n")
        }
    }
}

4.在build.gradle,新增如下内容

apply plugin: 'com.android.application'
apply plugin: 'com.tencent.tinker.patch'

android {
    compileSdkVersion 28
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs', 'src/main/jniLibs']
        }
    } 
} 
dependencies { 
    api("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true }
//    implementation("com.tencent.tinker:tinker-android-loader:${TINKER_VERSION}") { changing = true }
    annotationProcessor("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
    compileOnly("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
    implementation "com.android.support:multidex:1.0.1"
}

apply from: 'tinker-support.gradle'

5.新建MyApplicationLike

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);
    }
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        MultiDex.install(base);
        TinkerManager.setTinkerApplicationLike(this);
        TinkerManager.setUpgradeRetryEnable(true);
        TinkerInstaller.setLogIml(new MyLogImp());
        TinkerManager.installTinker(this);
        Tinker tinker = Tinker.with(getApplication());
    }
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
        getApplication().registerActivityLifecycleCallbacks(callback);
    }

}

6.改造application,添加如下部分

public class MyApplication extends TinkerApplication {
    public MyApplication() {
        super(ShareConstants.TINKER_ENABLE_ALL,"包名.MyApplicationLike");
    }

7.升级补丁

   TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(),
                Environment.getExternalStorageDirectory().getAbsolutePath()
                        + "/patch_signed_7zip2.apk");

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无极宝

你的鼓励是我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值