Tencent / tinker

Tinker is a hot-fix solution library for Android, it supports dex, library and resources update without reinstall apk.
Other
17.16k stars 3.33k forks source link

bugly上传说补丁包缺少参数 请不要直接关闭 我查询了所有issues没有找到答案 #1520

Open woaimi520 opened 3 years ago

woaimi520 commented 3 years ago

我查询了issues中的相关问题 但是还是没有得到解决 问题就是生成的补丁包在上传 bugly的时候提示没有 creatTIme等信息 应该是差了yapatch文件,yapatch文件这玩意生成需要 tinker-support里面的脚本 但是我的只有tinker,

另外: classpath 'com.android.tools.build:gradle:3.5.3' //只能到3.5.3 再高热更新就报错

下面是我的tinker.gradle配置(网上找的 )我本地测试没有问题 就是不能上传到bugly

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

ext { //for some reason, you may want to ignore tinkerBuild, such as instant run debug build? tinkerEnabled = true //基准apk路径 tinkerOldApkPath = "${bakPath}/app-release-0125-14-29-09.apk" //未开启混淆,则不需要填写 tinkerApplyMappingPath = "${bakPath}/" //基准apk中的R文件路径 tinkerApplyResourcePath = "${bakPath}/app-release-0125-14-29-09-R.txt" //如果你修复了res文件,需要指定你bug版本的R.txt文件 tinkerBuildFlavorDirectory = "${bakPath}/"

}

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 buildWithTinker() { return hasProperty("TINKER_ENABLE") ? TINKER_ENABLE : ext.tinkerEnabled } def getTinkerBuildFlavorDirectory() { return ext.tinkerBuildFlavorDirectory }

if (buildWithTinker()) { apply plugin: 'com.tencent.tinker.patch'

tinkerPatch {
    /**
     * 默认为null
     * 将旧的apk和新的apk建立关联
     * 从build / bakApk添加apk
     */
    oldApk = getOldApkPath()
    /**
     * 可选,默认'false'
     *有些情况下我们可能会收到一些警告
     *如果ignoreWarning为true,我们只是断言补丁过程
     * case 1:minSdkVersion低于14,但是你使用dexMode与raw。
     * case 2:在AndroidManifest.xml中新添加Android组件,
     * case 3:装载器类在dex.loader {}不保留在主要的dex,
     *          它必须让tinker不工作。
     * case 4:在dex.loader {}中的loader类改变,
     *          加载器类是加载补丁dex。改变它们是没有用的。
     *          它不会崩溃,但这些更改不会影响。你可以忽略它
     * case 5:resources.arsc已经改变,但是我们不使用applyResourceMapping来构建
     */
    ignoreWarning = true

    /**
     * 可选,默认为“true”
     * 是否签名补丁文件
     * 如果没有,你必须自己做。否则在补丁加载过程中无法检查成功
     * 我们将使用sign配置与您的构建类型
     */
    useSign = true

    /**
     * 可选,默认为“true”
     * 是否使用tinker构建
     */
    tinkerEnable = buildWithTinker()

    /**
     * 警告,applyMapping会影响正常的android build!
     */
    buildConfig {
        /**
         * 可选,默认为'null'
         * 如果我们使用tinkerPatch构建补丁apk,你最好应用旧的
         * apk映射文件如果minifyEnabled是启用!
         * 警告:你必须小心,它会影响正常的组装构建!
         */
        applyMapping = getApplyMappingPath()
        /**
         * 可选,默认为'null'
         * 最好保留R.txt文件中的资源id,以减少java更改
         */
        applyResourceMapping = getApplyResourceMappingPath()

        /**
         * 必需,默认'null'
         * 因为我们不想检查基地apk与md5在运行时(它是慢)
         * tinkerId用于在试图应用补丁时标识唯一的基本apk。
         * 我们可以使用git rev,svn rev或者简单的versionCode。
         * 我们将在您的清单中自动生成tinkerId
         */
        tinkerId = TINKER_ID.toInteger()

        /**
         * 如果keepDexApply为true,则表示dex指向旧apk的类。
         * 打开这可以减少dex diff文件大小。
         */
        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 {
        /**
         * 可选,默认'jar'
         * 只能是'raw'或'jar'。对于原始,我们将保持其原始格式
         * 对于jar,我们将使用zip格式重新包装dexes。
         * 如果你想支持下面14,你必须使用jar
         * 或者你想保存rom或检查更快,你也可以使用原始模式
         */
        dexMode = "jar"

        /**
         * 必需,默认'[]'
         * apk中的dexes应该处理tinkerPatch
         * 它支持*或?模式。
         */
        pattern = ["classes*.dex",
                   "assets/secondary-dex-?.jar"]
        /**
         * 必需,默认'[]'
         * 警告,这是非常非常重要的,加载类不能随补丁改变。
         * 因此,它们将从补丁程序中删除。
         * 你必须把下面的类放到主要的dex。
         * 简单地说,你应该添加自己的应用程序{@code tinker.sample.android.SampleApplication}
         * 自己的tinkerLoader,和你使用的类
         */
        loader = [
                //use sample, let BaseBuildInfo unchangeable with tinker
                "tinker.sample.android.app.BaseBuildInfo"
        ]
    }

    lib {
        /**
         * 可选,默认'[]'
         * apk中的图书馆应该处理tinkerPatch
         * 它支持*或?模式。
         * 对于资源库,我们只是在补丁目录中恢复它们
         * 你可以得到他们在TinkerLoadResult与Tinker
         */
        pattern = ["lib/*/*.so"]
    }

    res {
        /**
         * 可选,默认'[]'
         * apk中的什么资源应该处理tinkerPatch
         * 它支持*或?模式。
         * 你必须包括你在这里的所有资源,
         * 否则,他们不会重新包装在新的apk资源。
         */
        pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]

        /**
         *  可选,默认'[]'
         *  资源文件排除模式,忽略添加,删除或修改资源更改
         * *它支持*或?模式。
         * *警告,我们只能使用文件没有relative与resources.arsc
         */
        ignoreChange = ["assets/sample_meta.txt"]

        /**
         *  默认100kb
         * *对于修改资源,如果它大于'largeModSize'
         * *我们想使用bsdiff算法来减少补丁文件的大小
         */
        largeModSize = 100
    }

    packageConfig {
        /**
         *可选,默认'TINKER_ID,TINKER_ID_VALUE','NEW_TINKER_ID,NEW_TINKER_ID_VALUE'
         * 包元文件gen。路径是修补程序文件中的assets / package_meta.txt
         * 你可以在您自己的PackageCheck方法中使用securityCheck.getPackageProperties()
         * 或TinkerLoadResult.getPackageConfigByName
         * 我们将从旧的apk清单为您自动获取TINKER_ID,
         * 其他配置文件(如下面的patchMessage)不是必需的
         */
        configField("patchMessage", "tinker is sample to use")
        /**
         *只是一个例子,你可以使用如sdkVersion,品牌,渠道...
         * 你可以在SamplePatchListener中解析它。
         * 然后你可以使用补丁条件!
         */
        configField("platform", "all")
        /**
         * 补丁版本通过packageConfig
         */
        configField("patchVersion", "1.0.2")
    }
    //或者您可以添加外部的配置文件,或从旧apk获取元值
    //project.tinkerPatch.packageConfig.configField("test1", project.tinkerPatch.packageConfig.getMetaDataFromOldApk("Test"))
    //project.tinkerPatch.packageConfig.configField("test2", "sample")

    /**
     * 如果你不使用zipArtifact或者path,我们只是使用7za来试试
     */
    sevenZip {
        /**
         * 可选,默认'7za'
         * 7zip工件路径,它将使用正确的7za与您的平台
         */
        zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
        /**
         * 可选,默认'7za'
         * 你可以自己指定7za路径,它将覆盖zipArtifact值
         */

// path = "/usr/local/bin/7za" } }

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) {
                            try {
                                from new File(packageAndroidArtifact.outputDirectory.getAsFile().get(), variant.outputs.first().apkData.outputFileName)
                            } catch (Exception e) {
                                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"
                    from "${buildDir}/intermediates/symbol_list/${variant.dirName}/R.txt"
                    from "${buildDir}/intermediates/runtime_symbol_list/${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"
                }

            }
        }
    }
}

}

woaimi520 commented 3 years ago

我直接按照官网的配置配是会报一下错误 所以才在网上去找的配置 Caused by: org.gradle.internal.metaobject.AbstractDynamicObject$CustomMessageMissingMethodException: Could not find method tinkerSupport() for arguments [tinker_3k867nrbadqv1pdlrejtnp6yv$_run_closure2@6d599f14] on project ':app' of type org.gradle.api.Project.

scolia commented 3 years ago

我也遇到这样的问题, 我的解决方案是解压出来, 手动写yapatch, 然后再打包回去. 注意: 手动打包回去的时候, 确认下目录结构是否正确, 不能带一层顶级目录, 可以使用unzip -v命令来确认, 否则会找不到meta信息.