Android 即时功能:这种方法是否存在根本性缺陷?

Android instant feature: Is it something fundamentally flawed with this approach?

我试图让我的应用程序在从 google gradle 插件 3.1.4 更新到现在最新的 3.2.1 后再次构建,我认为该方法可能有问题我首先让我的 instantapp 工作,所以我 post 这个问题放弃或确认,在这种情况下要求不同的方法。

简单地说,我的应用程序是一个游戏,资产文件夹中有很多图形。为了使 instantapp 符​​合 Google Play 施加的 10 兆字节限制,我不得不删除其中一些我在即时版本中不需要的资产以及具有更高分辨率的资产,同时保留其余资产。此外,instantapp 的代码与已安装的代码略有不同,因此我重写了一些方法并在那里 类。

最小的 com.android.instantapp 模块似乎不能包含资产和代码,所以我创建了一个中间功能模块来代替它们。

所以,经过大量的努力(主要是使用 firebase 和 com.google.gms.google-services 插件)我想出了这种方法,最终与 gradle 插件的 3.1.4 版一起工作(com.android.tools.build:gradle:3.1.4)

唯一奇怪的是,我必须将标志 "baseFeature" 设置为 android_instant_feature 和 android_common 功能模块。但它奏效了,我终于可以发布我的游戏的即时版本,而安装版本也继续构建得很好。

现在,一旦我更新到 google gradle 插件的 3.2.1 版,即使我不做任何更改,问题也开始了。我在尝试构建项目时遇到了很多此类错误:

Unable to find a matching variant of project :android_common:
  - Variant 'debugApiElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found compatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debug' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Aar'.
      - Found org.gradle.usage 'java-api' but wasn't required.
  - Variant 'debugFeatureApiElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found compatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debugFeature' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Feature'.
      - Found org.gradle.usage 'java-api' but wasn't required.
  - Variant 'debugFeatureRuntimeElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found compatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debugFeature' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Feature'.
      - Found org.gradle.usage 'java-runtime' but wasn't required.
  - Variant 'debugRuntimeElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found compatible value 'debug'.
      - Found com.android.build.api.attributes.VariantAttr 'debug' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Aar'.
      - Found org.gradle.usage 'java-runtime' but wasn't required.
  - Variant 'releaseApiElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found incompatible value 'release'.
      - Found com.android.build.api.attributes.VariantAttr 'release' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Aar'.
      - Found org.gradle.usage 'java-api' but wasn't required.
  - Variant 'releaseFeatureApiElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found incompatible value 'release'.
      - Found com.android.build.api.attributes.VariantAttr 'releaseFeature' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Feature'.
      - Found org.gradle.usage 'java-api' but wasn't required.
  - Variant 'releaseFeatureRuntimeElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found incompatible value 'release'.
      - Found com.android.build.api.attributes.VariantAttr 'releaseFeature' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Feature'.
      - Found org.gradle.usage 'java-runtime' but wasn't required.
  - Variant 'releaseRuntimeElements':
      - Required com.android.build.api.attributes.BuildTypeAttr 'debug' and found incompatible value 'release'.
      - Found com.android.build.api.attributes.VariantAttr 'release' but wasn't required.
      - Required com.android.build.gradle.internal.dependency.AndroidTypeAttr 'Metadata' and found incompatible value 'Aar'.
      - Found org.gradle.usage 'java-runtime' but wasn't required.

在对 gradle 脚本进行一些调整后,包括从 android_instant_feature 模块中删除 baseFeature true 标志,我得到了一个更简短的错误,但我不能说我是否比前。错误是这样的:

Expected configuration ':android_instant_feature:debugFeatureCompileClasspath' to contain exactly one file, however, it contains no files.

现在我被困在这里,不知道该做什么或下一步该看哪里,所以我想知道我的方法是否存在根本性的缺陷,因为我是在反复试验后想出来的我不确定它是否合适,在这种情况下,我可以使用哪种其他方法来满足我的要求和来自 Google 的要求,无论它们是什么。

根据@TWL 的要求,这些是我的这些模块的 Gradle 文件。我已经用 com.myapp.id 替换了真实的应用程序 ID,剩下的就是我所拥有的,包括很多我认为与此事无关的 libGDX 的东西,尽管我更愿意把它留在那里精度名称:

android_common:

apply plugin: "com.android.feature"

configurations { natives }

android {
    baseFeature true
    compileSdkVersion 28
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
        }

        androidTest.setRoot('tests')
    }
    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        sourceSets {
            all {
                manifest.srcFile "AndroidManifest.xml"
            }
        }
        multiDexEnabled true
    }
    buildTypes {
        release {
            setMinifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    buildToolsVersion '28.0.2'
    lintOptions {
        abortOnError false
    }
    dexOptions {
        jumboMode true
    }
}

dependencies {
    application project(':android')
    implementation project(':core')
    api 'com.android.support:multidex:1.0.3'
    implementation "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi"
    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a"
    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a"
    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86"
    natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64"
    implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
    natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi"
    natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi-v7a"
    natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a"
    natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86"
    natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86_64"
    implementation "com.android.billingclient:billing:$playBillingLibVersion"
    implementation "com.google.firebase:firebase-core:16.0.3"
    implementation "com.google.firebase:firebase-ads:15.0.1"

    implementation fileTree(include: ['*.jar'], dir: 'libs')
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'com.google.guava:guava:24.0-android'
}

task copyAndroidNatives() {
    file("libs/armeabi/").mkdirs();
    file("libs/armeabi-v7a/").mkdirs();
    file("libs/arm64-v8a/").mkdirs();
    file("libs/x86_64/").mkdirs();
    file("libs/x86/").mkdirs();

    configurations.natives.files.each { jar ->
        def outputDir = null
        if (jar.name.endsWith("natives-arm64-v8a.jar")) outputDir = file("libs/arm64-v8a")
        if (jar.name.endsWith("natives-armeabi-v7a.jar")) outputDir = file("libs/armeabi-v7a")
        if (jar.name.endsWith("natives-armeabi.jar")) outputDir = file("libs/armeabi")
        if (jar.name.endsWith("natives-x86_64.jar")) outputDir = file("libs/x86_64")
        if (jar.name.endsWith("natives-x86.jar")) outputDir = file("libs/x86")
        if (outputDir != null) {
            copy {
                from zipTree(jar)
                into outputDir
                include "*.so"
            }
        }
    }
}

android_instant_feature:

apply plugin: "com.android.feature"

android {
//    baseFeature true
    buildToolsVersion "28.0.2"
    compileSdkVersion 28
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ["${project(':android_common').projectDir}/res"]
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
        }
    }
    defaultConfig {
        versionCode 4031
        versionName "4.1.2.1i"
        minSdkVersion 16
        targetSdkVersion 28
        multiDexEnabled true
    }

    dexOptions {
        jumboMode true
    }

    // Proguard configuration
    buildTypes {
        release {
            //minifyEnabled true will turn proguard ON
//            minifyEnabled true
            //proguardFiles let you add your own proguard rules ('proguard-project.txt') in this case, as its already created by gdx-setup
//            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
        }
        debug {}
    }


    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    productFlavors {
    }
    lintOptions {
        abortOnError false
    }
}

task run(type: Exec) {
    def path
    def localProperties = project.file("../local.properties")
    if (localProperties.exists()) {
        Properties properties = new Properties()
        localProperties.withInputStream { instr ->
            properties.load(instr)
        }
        def sdkDir = properties.getProperty('sdk.dir')
        if (sdkDir) {
            path = sdkDir
        } else {
            path = "$System.env.ANDROID_HOME"
        }
    } else {
        path = "$System.env.ANDROID_HOME"
    }

    def adb = path + "/platform-tools/adb"
    commandLine "$adb", 'shell', 'am', 'start', '-n', 'com.marzoa.ruletafree/com.marzoa.ruletafree.AndroidLauncher'
}

dependencies {
    implementation project(":core")
    feature project(":android_common")
    api 'com.android.support:multidex:1.0.3'
    implementation "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
    implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
    implementation "com.google.firebase:firebase-core:16.0.3"
    implementation 'com.google.android.gms:play-services-instantapps:16.0.0'

    implementation fileTree(dir: 'libs', include: ['*.jar'])
}

// ADD THIS AT THE BOTTOM
apply plugin: 'com.google.gms.google-services'

android_instant:

apply plugin: 'com.android.instantapp'

android {
    defaultConfig {
        applicationId "com.myapp.id"
        minSdkVersion 16
        targetSdkVersion 28
        multiDexEnabled true
    }
}

dependencies {
    implementation project(":android_common")
    implementation project(":android_instant_feature")
}

android:

apply plugin: "com.android.application"
apply plugin: 'io.fabric' // Needed by crashlytics. DO NOT REMOVE.

android {
    buildToolsVersion "28.0.2"
    compileSdkVersion 28
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ["${project(':android_common').projectDir}/res"]
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
        }

        androidTest.setRoot('tests')
    }
    defaultConfig {
        applicationId "com.myapp.id"
        versionCode 4032
        versionName "4.1.2.1"
        minSdkVersion 16
        targetSdkVersion 28
        multiDexEnabled true
    }
    dexOptions {
        jumboMode true
    }
    // Proguard configuration
    buildTypes {
        release {
            //minifyEnabled true will turn proguard ON
            minifyEnabled true
            //proguardFiles let you add your own proguard rules ('proguard-project.txt') in this case, as its already created by gdx-setup
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-project.txt'
        }
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    productFlavors {
    }
    lintOptions {
        abortOnError false
    }
}

task importPuzzles(type: Exec) {
    // TODO
}

task run(type: Exec) {
    def path
    def localProperties = project.file("../local.properties")
    if (localProperties.exists()) {
        Properties properties = new Properties()
        localProperties.withInputStream { instr ->
            properties.load(instr)
        }
        def sdkDir = properties.getProperty('sdk.dir')
        if (sdkDir) {
            path = sdkDir
        } else {
            path = "$System.env.ANDROID_HOME"
        }
    } else {
        path = "$System.env.ANDROID_HOME"
    }

    def adb = path + "/platform-tools/adb"
    commandLine "$adb", 'shell', 'am', 'start', '-n', 'com.marzoa.ruletafree/com.myapp.id.AndroidLauncher'
}

dependencies {
    implementation project(":core")
    implementation project(":android_common")
    api 'com.android.support:multidex:1.0.3'
    implementation "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
    implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
    implementation "com.android.billingclient:billing:$playBillingLibVersion"
    implementation "com.google.firebase:firebase-core:16.0.3"
    implementation "com.google.firebase:firebase-ads:15.0.1"
    implementation 'com.crashlytics.sdk.android:crashlytics:2.9.5'

    implementation fileTree(include: ['*.jar'], dir: 'libs')
}

// ADD THIS AT THE BOTTOM
apply plugin: 'com.google.gms.google-services'

非常感谢!

感谢您提供 gradle 文件,看起来您的 gradle 依赖项被混淆了。

包含 baseFeature truecom.android.feature 模块应该是其依赖项也包含 application project()feature project() 的模块。这是你错误的主要原因:

Expected configuration ':___:debugFeatureCompileClasspath' to contain exactly one file, however, it contains no files.

因此,从您提供的内容来看,您的 application project()feature project() 似乎是分开的。

要解决此问题,您的“baseandroid_common 应该是:

application project(':android')
feature project(":android_instant_feature")

你的“非基础特征android_instant_feature应该是:

implementation project(':android_common)

这不会改变项目的整体结构。

  • 您的 com.android.application 将与以下库一起编译:coreandroid_common
  • 您的 com.android.instantapp 将与即时应用程序 apks 一起编译:android_common.apkandroid_instant_feature.apk

现在,为什么它以前使用 2x baseFeature 可以工作?不知道,应该没有...

曾经有一个页面@https://g.co/instantapps that explained the structure of instant apps, but you can refer to one of my previous posts that touches on it for an overview and reference links: How can i access an activity from another feature module

旁注:我注意到您的模块中有一个 implementation project(":core") 通用(减去 instantapp)。如果那纯粹是一个像 com.android.library 这样的库,那么你不应该那样设置它。您实际上可以将它放在您的 base 中,如:api project(":core") 并删除其他地方的所有其他引用。将其声明为 api 意味着任何也实现了该基础的模块也将暴露于此依赖项。 (试一试,根据你的 core 拥有的其他库,在极少数情况下会出现错误,但请尝试查看 )。