为同一类的不同版本构建不同的变体

76

我有一个项目,结构如下:

project/
   |
   |---src/
        |---flavorA2/
        |      |
        |      |---java/
        |      |     |---com.abc.flavorA.mk2
        |      |                 |-----classA.java
        |      |                 |-----classB.java
        |      |---res/
        |      |---AndroidManifest.xml
        |
        |---main
        |      |---java/
        |      |     |---com.abc.flavorA
        |      |                 |-----classA.java
        |      |                 |-----classB.java
        |      |                 |-----classC.java
        |      |                 |-----classD.java
        |      |---res/
        |      |    |---drawable/
        |      |    |---layout/
        |      |    |---values/
        |      |         
        |      |---AndroidManifest.xml
        |
        |---flavorA

flavorA会完全使用main的源代码和资源,而flavorA2classAclassB做了一些小修改,并且包名也改成了com.abc.flavorA.mk2

我的build.gradle文件如下:

...
buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
    productFlavors {
        flavorA2 {
            packageName "com.abc.flavorA.mk2"
            versionCode 2
            versionName "1.0.1"
        }

        flavorA {
            packageName "com.abc.flavorA"
        }
    }
...

我选择了flavorA2构建版本来运行代码。然而,运行结果显示gradle仍从main中选择类(classAclassB),而不是使用flavorA2内部的更改版本。

这里有什么我忽略的吗?


packageName现在无法工作。 - Naveed Ahmad
@dumbfingers,在Android Studio中是否有任何快捷方式可以生成项目结构,就像您在顶部的问题中添加的那样? - TejaDroid
4个回答

128

由于您将这些类放在2个不同的包中,它们是完全不同的类。因此,这些类并没有互相替换。

使用flavor,您无法覆盖类文件。因此,实现您想要的一种方法是将这些类从main移到flavorA中。

所以您会得到像这样的结果:

project/
   |
   |---src/
        |---flavorA2/
        |      |
        |      |---java/
        |      |     |---com.abc
        |      |                 |-----classA.java
        |      |                 |-----classB.java
        |      |---res/
        |      |---AndroidManifest.xml
        |
        |---main/
        |      |---java/
        |      |     |---com.abc.flavorA
        |      |                 |-----classC.java
        |      |                 |-----classD.java
        |      |---res/
        |      |    |---drawable/
        |      |    |---layout/
        |      |    |---values/
        |      |         
        |      |---AndroidManifest.xml
        |
        |---flavorA/
        |      |---java/
        |      |     |---com.abc
        |      |                 |-----classA.java
        |      |                 |-----classB.java

这样,无论你选择了哪种口味,只有一个版本的ClassA和ClassB会被显示出来。


1
在我的情况下,flavors 中的类对 main 中的类不可见。有什么解决办法吗? - Barışcan Kayaoğlu
1
如果您正在构建它们所在的“风味”,则它们应该是可见的。如果您能在另一个SO问题中发布有关设置和构建脚本的更多详细信息,那将会很有帮助。 - Hassan Ibraheem
1
正确,但该语句是在谈论构建变体。这意味着您不能在构建类型(调试、发布...)和口味中同时定义相同的类,因为默认情况下Gradle会将来自主要口味、当前口味和构建类型的源代码组合在一起,所以这三个位置不应具有冲突的类定义。 - Hassan Ibraheem
3
@HassanIbraheem 很好的文章!你知道是否有一种方法可以在不重复的情况下为两个口味使用相同的Java类吗?我的用例是:我有3个口味,但其中两个应该使用相同的Java类,只有一个是不同的。 - Fahim
2
@HassanIbraheem 这对我来说是一个问题,因为它使得维护源代码更加困难。每次编辑都需要切换口味,并且在进行编辑时还要复制粘贴相同的代码。除了dagger之外,没有更好的选择吗?例如,我认为像@Target(flavor='flavorA')这样的注释方法可以覆盖该口味下具有相同名称的方法,这是一个更好的解决方案。您知道是否有任何现有的库可以实现这一点吗? - Saifur Rahman Mohsin
显示剩余14条评论

12

在主要的构建变体中,Class A 是 com.abc.flavorA.classA,而在 flavorA2 中它是 com.abc.flavorA.mk2.classA。这两个都是完全限定类名,因此是两个不同的类。

你不能真正地重写整个类。根据你想要做什么,你可能想要研究一下 BuildConfig 机制——简而言之,这是由构建系统生成的一个类,它可以具有随构建类型和 flavor 变化的值或语句。你可以在运行时查看该类中的常量并改变其行为。

有关语法的更多信息,请参见 Android Studio Update 0.4.0 could not find buildConfig(),但简而言之,它看起来像这样:

productFlavors {
    flavor {
      buildConfigField "boolean", "MY_FLAG", "true"
    }
}

有没有办法让 flavorA2 的类“覆盖” main 中的类? - dumbfingers
好的,谢谢!我曾经尝试过使用不同的资源风格来实现相同的代码库,这种方式可以奏效。但是,如果我的理解是正确的话,这种方法似乎无法应用于代码上。 - dumbfingers
1
你可以通过以下方式访问buildConfig字段:if(BuildConfig.MY_FLAG) { ... } - Christian Schulzendorff

6

你需要在构建文件中指定 sourceSets。你需要修改你的目录结构,使得只有文件夹名称不同,java目录下的所有内容都应该相同,因此从类名中删除 mk2。我不确定语法是否完全正确,但它应该像这样:

android {
    sourceSets {
        flavorA {
            java {
                srcDirs = ['src/flavorA/java']
            }
        }

        flavorA2 {
            java {
                srcDirs = ['src/flavorA2/java']
            }
        }
    }
}

1
这应该是不必要的,以实现他在示例源代码中所拥有的内容,而且更重要的是,您正在重命名所有文件和文件夹,这也是不必要的。 - Scott Barta
我认为除了productFlavors{}块之外,我们还可以为每个产品风味指定sourceSets{}。 - ashishdhiman2007

1

我在这里发布答案,希望有人能够实现相同的功能。在我的情况下,我有两个静态常量,想要根据不同的版本分配不同的值。因此,我创建了一个对象并声明了常量,然后通过创建不同的源集将对象放置在两个版本中。

假设版本为“Variant_A”和“Variant_B”

Variant_A/java/package_name.util.variantspec

object ConfigConstant {
    // Carousels
    /**
     * Changing PERCENT_SCALE_X_IN or PERCENT_SCALE_Y_IN should also change scale animation
     * percentages accordingly.
     */
    const val PERCENT_SCALE_X_IN = 0.10 // 10% percentage
    const val PERCENT_SCALE_Y_IN = 0.10 // 10% percentage
}

Variat_B/java/package_name.util.variantspec

object ConfigConstant {
    // Carousels
    /**
     * Changing PERCENT_SCALE_X_IN or PERCENT_SCALE_Y_IN should also change scale animation
     * percentages accordingly.
     */
    const val PERCENT_SCALE_X_IN = 0.15 // 15% percentage
    const val PERCENT_SCALE_Y_IN = 0.15 // 15% percentage
}

从其他类中访问

class VerticalCarouselAdapter() {
   companion object {
        /**
         * Changing PERCENT_SCALE_X_IN or PERCENT_SCALE_Y_IN should also change scale animation
         * percentages accordingly.
         */
        const val PERCENT_SCALE_X_IN = ConfigConstant.PERCENT_SCALE_X_IN
        const val PERCENT_SCALE_Y_IN = ConfigConstant.PERCENT_SCALE_Y_IN
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接