Tencent / Shadow

零反射全动态Android插件框架
BSD 3-Clause "New" or "Revised" License
7.44k stars 1.3k forks source link

请注意每个插件apk构建时都需要apply plugin: 'com.tencent.shadow.plugin' #1117

Closed lilidejing closed 1 year ago

lilidejing commented 1 year ago

编译环境:jdk11,gradle版本:classpath "com.android.tools.build:gradle:4.0.2"distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip 问题背景: 我们项目需要将shadow老版本升级为新版本,经过一系列操作,和通过上次提问的解答(#1114),执行gradlew packageDebugPlugin命令,本地编译插件已经成功。

但是宿主APP启动插件的时候,报如下错误: java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: java.lang.Error: 请注意每个插件apk构建时都需要apply plugin: 'com.tencent.shadow.plugin'

查找原因参考了issues中的 #871 #916,发现我的问题似乎和他们不太一样。因为我gradle文件中没有申明 渠道 flavorDimensions "product" 相关代码,想反,我是把 gradle文件中 flavorDimensions "product" 这句代码注释掉了(shadow升级前有这句)。

所以项目中shadow依赖版本升级前编译和升级后编译,gradle文件的主要区别就是 新的把 gradle文件中 flavorDimensions "product" 这句代码注释掉了,可以编译成功,但编译出来的plugin-debug.apk 解压查看所有的dex文件中都找不到 com.tencent.shadow 这个目录 。请问,这可能是什么原因导致的?找了一天多原因了,求解答,谢谢!

shifujun commented 1 year ago

你没说清楚你的场景和 #916 有什么区别。也没提供 #916 中提到的相关信息。

lilidejing commented 1 year ago

你没说清楚你的场景和 #916 有什么区别。也没提供 #916 中提到的相关信息。

以下是我们的gradle文件内容,和#916插件的gradle文件的区别是: 1,少了一句 flavorDimensions(flavorDimensionList)。 2,他的gradle版本是7.0.2,我的版本是6.6.1 但是我把 flavorDimensions(flavorDimensionList) 这句加上打包,打包的插件apk还是一样找不到 com.tencent.shadow 目录。 写法和demo是一样的,demo打包出来的可以,但到项目里面打包出来的就不行。shadow版本升级为最新版本,还有其他需要注意的地方吗?

apply plugin: 'com.android.application'
apply plugin: 'com.tencent.shadow.plugin'
//greendao
apply plugin: 'org.greenrobot.greendao' // apply plugin
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
apply from: 'and_res_guard.gradle'
//apply from: 'tinker_patch.gradle'

android {
    compileSdkVersion rootProject.ext.android.compileSdkVersion
    defaultConfig {
        applicationId "com.kye.pad"
        minSdkVersion rootProject.ext.android.minSdkVersion
        targetSdkVersion rootProject.ext.android.targetSdkVersion
        versionCode 1
        versionName "1.0"
        // 解决Dex超出方法数的限制问题
        multiDexEnabled true
        multiDexKeepProguard file("tinker_multidexkeep.pro")
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        // 支持 Vector Drawables 以及 Animated Vector Drawables
        vectorDrawables.useSupportLibrary = true

        ndk {
            abiFilters /*"armeabi",*/ "armeabi-v7a"/*, "x86"*/
        }
        manifestPlaceholders = [
                JPUSH_APPKEY : "123456",
                JPUSH_CHANNEL: "default",
        ]
    }

    repositories {
        flatDir {
            dirs 'libs'
        }
    }

    dexOptions {
        jumboMode = true
        keepRuntimeAnnotatedClasses false
    }

    // Data Binding Library(数据绑定框架)
    dataBinding.enabled = true

    signingConfigs {

        debugConfig {
            keyAlias DEBUG_KEY_ALIAS
            keyPassword DEBUG_KEY_PASSWROD
            storeFile file(DEBUG_STORE_FILE)
            storePassword DEBUG_STORE_PASSWORD
        }

        releaseConfig {
            keyAlias DEBUG_KEY_ALIAS
            keyPassword DEBUG_KEY_PASSWROD
            storeFile file(DEBUG_STORE_FILE)
            storePassword DEBUG_STORE_PASSWORD
        }

        iwrist {
            keyAlias 'app3'
            keyPassword '123123'
            storeFile file('./keystore/apksign/app3.jks')
            storePassword '123123'
        }
    }

    buildTypes {

        // (测试环境,不带混淆,可打印日志)
        debug {
            buildConfigField "boolean", "isDebug", "true"
            buildConfigField "boolean", "IS_GREENDAO_ENCRYPTED", "false"//GreenDao数据库是否加密
            buildConfigField "int", "pluginVersion", String.valueOf(rootProject.ext.android.pluginVersion)

            resValue "string", "app_name",  "插件debug"

            //混淆
            minifyEnabled false
            shrinkResources false
            debuggable true

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

            signingConfig signingConfigs.iwrist
        }

        // (带混淆,不可打印日志)
        release {
            buildConfigField "boolean", "isDebug", "false"
            buildConfigField "boolean", "IS_GREENDAO_ENCRYPTED", "false"//GreenDao数据库是否加密
            buildConfigField "int", "pluginVersion", String.valueOf(rootProject.ext.android.pluginVersion)
            resValue "string", "app_name", "插件release"

            //混淆
            minifyEnabled false
            shrinkResources false

            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

        }
    }

    sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/greendao/java']
            jni.srcDirs = []                 //禁止自动调用ndk-build命令
            jniLibs.srcDir 'src/main/libs'  // 设置目标的so存放路径
        }
    }
//    flavorDimensions "product"

    //渠道
    productFlavors {

        iwrist {

            applicationId "com.iwrist.host.appid"

            versionCode 8
            versionName '6.'+ rootProject.ext.android.appVersionName + '.sku'
            productFlavors.iwrist.signingConfig signingConfigs.iwrist

        }

    }

    //APP打包命名
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            if (outputFile != null && outputFile.name.endsWith('.apk')) {
                def fileName
                fileName = "app-plugin-debug.apk"
                output.outputFileName = fileName
            }
        }
    }

    //使用JavaJDK1.8版本
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    //kotlin支持JDK1.8
    kotlinOptions { jvmTarget = '1.8' }

    repositories {
        flatDir {
            dirs file('libs')
        }
    }

    packagingOptions {
        exclude 'META-INF/proguard/coroutines.pro'
        exclude 'META-INF/proguard/androidx-annotations.pro'
    }

}

//这段buildscript配置的dependencies是为了apply plugin: 'com.tencent.shadow.plugin'能找到实现
buildscript {
    repositories {
        jcenter()
        google()
        mavenLocal()
        maven { url 'http://nexus.ms.com:8081/repository/maven-public/' }
    }

    dependencies {
        classpath "com.tencent.shadow.core:gradle-plugin:$shadow_version"
    }
}

// greenDao 配置
greendao {
    schemaVersion 20//数据库版本号
    daoPackage 'com.kye.greendao.gen'//设置DaoMaster、DaoSession、Dao包名
    targetGenDir 'src/main/greendao/java'//设置DaoMaster、DaoSession、Dao目录
}

dependencies {

    //    greenDao
    api rootProject.ext.dependencies["greendao"]
    api rootProject.ext.dependencies["greendao-upgrade-helper"]
    api rootProject.ext.dependencies["android-database-sqlcipher"]

    //  加载Android so文件的一个库 ,Github:https://github.com/KeepSafe/ReLinker
    api rootProject.ext.dependencies["relinker"]

    //vlayout
    implementation('com.alibaba.android:vlayout:1.2.2@aar') {
        transitive = true
     //   exclude group: 'androidx.core', module: 'core'
    }

    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation rootProject.ext.dependencies["kotlinx-coroutines-core"]
    implementation rootProject.ext.dependencies["kotlinx-coroutines-android"]

    //tinker的核心库
//    implementation ('com.tencent.tinker:tinker-android-lib:1.9.14.6')

    /* matrix */
    implementation group: "com.tencent.matrix", name: "matrix-commons", version: "$matrix", changing: true
    implementation group: "com.tencent.matrix", name: "matrix-android-lib", version: "$matrix", changing: true
    implementation group: "com.tencent.matrix", name: "matrix-android-commons", version: "$matrix", changing: true
    implementation group: "com.tencent.matrix", name: "matrix-trace-canary", version: "$matrix", changing: true

    //Shadow Transform后业务代码会有一部分实际引用runtime中的类
    //如果不以compileOnly方式依赖,会导致其他Transform或者Proguard找不到这些类
    compileOnly "com.tencent.shadow.core:runtime:$shadow_version"
    implementation 'com.google.android:flexbox:1.0.0'

    implementation rootProject.ext.dependencies["utilcode"]
    implementation rootProject.ext.dependencies["glide"]
    annotationProcessor rootProject.ext.dependencies["glide-compiler"]
    // okhttp
    implementation (rootProject.ext.dependencies["glide-okhttp3-integration"]) {
        exclude group: 'glide-parent'
    }
    implementation rootProject.ext.dependencies["gson"]

    implementation rootProject.ext.dependencies["mmkv"]

    implementation rootProject.ext.dependencies["common-styles"]
    implementation rootProject.ext.dependencies["common-utils"]
    implementation rootProject.ext.dependencies["common-widget"]

    // If you're using Java
    annotationProcessor "com.sun.xml.bind:jaxb-core:2.3.0.1"
    annotationProcessor "javax.xml.bind:jaxb-api:2.3.1"
    annotationProcessor "com.sun.xml.bind:jaxb-impl:2.3.2"
    annotationProcessor "org.glassfish.jaxb:jaxb-runtime:2.3.1"
}

apply plugin: 'com.tencent.matrix-plugin'

matrix {
    trace {
        enable = false
        baseMethodMapFile = "${project.projectDir}/matrixTrace/methodMapping.txt"
        blackListFile = "${project.projectDir}/matrixTrace/blackMethodList.txt"
    }
}

/**
 * 统一support版本库
 * https://blog.csdn.net/xx326664162/article/details/71488551
 */
configurations.all {

    resolutionStrategy.eachDependency { DependencyResolveDetails details ->
        def requested = details.requested
        if (requested.group == 'androidx.multidex') {
            if (!requested.name.contains("multidex")) {
            //    details.useVersion rootProject.ext.dependVersion.support
                details.useVersion '2.0.0'
            }
        }

        if (requested.group == 'androidx.core') {
            if (!requested.name.contains("androidx")) {
               // details.useVersion rootProject.ext.dependVersion.support
                details.useVersion "1.1.0"
            }
        }

        if (requested.group == 'androidx.appcompat') {
            details.useVersion "1.0.0"
        }
        if (requested.group == 'androidx.recyclerview') {
            details.useVersion "1.0.0"
        }
    }

}

//生成时间
static def releaseTime() {
    def format = new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
    return format
}
shadow {
    packagePlugin {
        pluginTypes {
            debug {
                loaderApkConfig = new Tuple2('ky-loader-debug.apk', ':ky-loader:assembleDebug')
                runtimeApkConfig = new Tuple2('ky-runtime-debug.apk', ':ky-runtime:assembleDebug')
                pluginApks {
                    pluginApk1 {
                        businessName = 'ky-plugin-allocate'

                        partKey = 'ky-plugin-allocate'
                        buildTask = 'assembleIwristDebug'

                        apkPath = 'app/build/outputs/apk/iwrist/debug/app-plugin-debug.apk'
                    }
                }
            }

            release {
                loaderApkConfig = new Tuple2('ky-loader-release-unsigned.apk', ':ky-loader:assembleRelease')
                runtimeApkConfig = new Tuple2('ky-runtime-release-unsigned.apk', ':ky-runtime:assembleRelease')
                pluginApks {
                    pluginApk1 {
                        businessName = 'ky-plugin-allocate'
                        partKey = 'ky-plugin-allocate'
                        buildTask = 'assembleIwristRelease'

                        apkPath = 'app/build/outputs/apk/iwrist/release/app-plugin-debug.apk"
                    }
                }
            }
        }

        loaderApkProjectPath = 'ky-loader'

        runtimeApkProjectPath = 'ky-runtime'

        version = rootProject.ext.android.pluginVersion

        compactVersion = [1, 2, 3]
        uuidNickName = "1.22083101.5"
        uuid = "1.22083101.5"
    }
}
shifujun commented 1 year ago

这个报错和flavorDimensions一点关系都没有,你好像根本没看916似的。

lilidejing commented 1 year ago

这个报错和flavorDimensions一点关系都没有,你好像根本没看916似的。

看了,你的意思是,我的问题和916一样吗,是因为本地的shadow依赖版本可能用错了?但是我用官方的demo依赖的是同一本地shadow版本,官方的demo打包出来的 app-plugin-debug.apk 是正常的。

shifujun commented 1 year ago

PluginManifest.java是否有生成?是否有编译?是否有混淆?是否有打包?

lilidejing commented 1 year ago

PluginManifest.java是否有生成?是否有编译?是否有混淆?是否有打包?

谢谢耐心回答,辛苦了! 1,PluginManifest.java没有生成。问题已经说了,打包出来的包查看dex文件,连com.tencent.shadow 这个目录都没。 2,有编译 3,没有混淆 4,有打包,用的 gradlew packageDebugPlugin 这个命令。 最后项目根目录下会生成一个 build目录,build目录下面会有一个plugin-debug.zip 文件。

我一开始的问题就是想问,用 gradlew packageDebugPlugin 这个命令打包出来的plugin-debug.zip 文件中的plugin-app-plugin-debug.apk解压后,查看dex文件中,为什么PluginManifest.java和其他shadow相关代码没有生成?可能和什么有关?

shifujun commented 1 year ago

transform和plugin manifest都没有生效才会有dex中完全没有shadow类型引用的效果吧。所以可能性有很多,可以随便猜。

如果你想解决问题,就不要让人猜你的代码。你的私有代码不能开源是正常的。但是要来开源项目交流就要有开源的代码。所以你要么来询问shadow代码中哪里你debug有困难。要么你修改一下demo复现问题,以此反馈shadow存在bug。通过这两种方式间接的解决你私有项目存在的问题。shadow也能从你的反馈中改进、获益。

lilidejing commented 1 year ago

transform和plugin manifest都没有生效才会有dex中完全没有shadow类型引用的效果吧。所以可能性有很多,可以随便猜。

如果你想解决问题,就不要让人猜你的代码。你的私有代码不能开源是正常的。但是要来开源项目交流就要有开源的代码。所以你要么来询问shadow代码中哪里你debug有困难。要么你修改一下demo复现问题,以此反馈shadow存在bug。通过这两种方式间接的解决你私有项目存在的问题。shadow也能从你的反馈中改进、获益。

明白。 用demo复现了,发现只要把gradle文件中productFlavors 下 plugin 改为其他名称,且配合修改shadow...pluginApk1下buildTask和apkPath,打包出来的插件,dex文件中,就会没有PluginManifest.java文件。 而名productFlavors 下的名称用plugin打包出来就正常。 请问 productFlavors 下的渠道名称只能用 写死用 plugin 吗? 打包出来没有PluginManifest.java文件的demo如下:https://github.com/lilidejing/plugin-project

shifujun commented 1 year ago

有一个简单的demo这就很容易发现问题了。

你没有指定iwirst这个productFlavor的dimension,所以相当于给唯一的、shadow创建的dimension又新增了一个flavor,结果变成了normalpluginiwirst三个平行的了。shadow显然不会对iwirst添加任何修改。

应该这样写: https://github.com/Tencent/Shadow/blob/8d9c7a7f21629bf60ef7871323bb0f1512738e44/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle#L49-L59

所以其实你和#871 是同一个问题。只是你有个确实让人想不到的操作,你把assembleIwristDebug写到buildTask去了。所以执行packageDebugPlugin实际上压根没执行过任何Plugin相关的构建。这里确实没有代码检测buildTask到底是不是一个Plugin的assemble任务。

lilidejing commented 1 year ago

有一个简单的demo这就很容易发现问题了。

你没有指定iwirst这个productFlavor的dimension,所以相当于给唯一的、shadow创建的dimension又新增了一个flavor,结果变成了normalpluginiwirst三个平行的了。shadow显然不会对iwirst添加任何修改。

应该这样写:

https://github.com/Tencent/Shadow/blob/8d9c7a7f21629bf60ef7871323bb0f1512738e44/projects/test/gradle-plugin-agp-compat-test/stub-project/build.gradle#L49-L59

所以其实你和#871 是同一个问题。只是你有个确实让人想不到的操作,你把assembleIwristDebug写到buildTask去了。所以执行packageDebugPlugin实际上压根没执行过任何Plugin相关的构建。这里确实没有代码检测buildTask到底是不是一个Plugin的assemble任务。

谢谢! 根据你的思路,我将productFlavors 改为如下:

flavorDimensions(*flavorDimensionList, 'DimensionA')
// 将插件applicationId设置为和宿主相同
productFlavors {
        /*plugin {
            applicationId "com.tencent.shadow.sample.host"
        }*/

        iwirst {
            dimension 'DimensionA'
            applicationId "com.tencent.shadow.sample.host"
        }

}

buildTask 改为如下:

  buildTask = 'assemblePluginIwirstDebug'

最后执行 packageDebugPlugin 打包是正常的。 改动后的gradle文件:gradle

再次感谢!