Gradle风格和main中重复类的解决方法 [英] Workaround for duplicate classes in Gradle flavors and main

查看:138
本文介绍了Gradle风格和main中重复类的解决方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想我的问题很普遍.我有一个相当大的gradle代码库,从中我可以使用产品风格来生成自定义版本.这些产品口味通常需要来自src\main\java的一个或多个类的定制版本.

I imagine my problem is quite common. I have a fairly large gradle code base, from which I produce customised versions using product flavors. These product flavors will often require a customised version of one or more classes from src\main\java.

我已经阅读了Gradle文档,还遇到了以下问题,以解决同一问题:
使用Build Flavors-构建源文件夹并正确build.gradle
为同一类的不同版本构建风味

I have read the Gradle documentation and have also come across the following questions looking at the same issue:
Using Build Flavors - Structuring source folders and build.gradle correctly
Build flavors for different version of same class

我理解为什么您不能在src\main\java和风味中定义相同的类,但是将类从src\main\java移至产品风味中的解决方案有一个很大的缺点.将类从src\main\java移到最新样式以对其进行自定义时,还需要将该类的原始未定制版本的副本移到其他所有以前的产品样式中,否则它们将不再构建.

I understand why you can't define the same class in src\main\java and also in your flavors, however the solution of moving the class from src\main\java into your product flavor has a fairly major drawback. When you move a class from src\main\java into your latest flavor to customise it, you also need to move a copy of the original uncustomised version of that class into every other previous product flavor or they won't build anymore.

您可能每次只需要调整原始类别中的一两个类别(然后将这些类别重新分配到flavor目录中),但是随着时间的流逝,将建立移动的类别数量,而剩余的类别数量将保持不变.每次执行此操作时,src\main\java都会减少.最终,大多数类都将具有风味(即使大多数将是原始版本),而src\main\java几乎是空的,这违反了整个Gradle构建结构的目的.
此外,您还需要保留一个默认"样式,您可以在每次启动新样式时进行克隆,这样就可以知道您是按照原始代码库从所有类开始的.

You may only need to tweak one or two different classes from the originals each time (and then have to re-distrubute those classes to the flavor directories), but over time the number of classes moved will build and the number remaining in src\main\java will decrease every time you have to do this. Eventually most of the classes will be in the flavors (even though the majority will be copies of the originals) and src\main\java will be almost empty, kind of defeating the purpose of the whole Gradle build structure.
Additionally you'll need to keep a "default" flavor that you can clone each time you start a new flavor, so you know you're starting with all classes as per your original code base.

使用BuildConfig中的字段来定义是否应使用自定义类:

Use fields in the BuildConfig to define if custom class should be used or not:

buildConfigField 'boolean', 'CUSTOM_ACTIVITY_X', 'true'

然后您可以使用以下代码:

You can then use code such as:

final Intent intent = new Intent();
...
if (BuildConfig.CUSTOM_ACTIVITY_X) {
   intent.setClass(ThisActivity.this, CustomActivityX.class); 
} else {
   intent.setClass(ThisActivity.this, DefaultActivityX.class);  
}
startActivity(intent);

每种口味都仍需要一个CustomActivityX副本,但是在您不会使用它的口味中,它只能是虚拟的空类.这意味着您的类的默认版本始终保留在src\main\java中.

Every flavor will still need a copy CustomActivityX, but it can just be a dummy empty class in flavors where you know it will not be used. This means your default versions of the classes are always retained in src\main\java.

在尝试摆脱每种其他口味的虚拟CustomActivityX的需求时,我一直在考虑使用Class.forName().
例如:

While trying to get rid of the need for a dummy CustomActivityX in every other flavor, I've looked at using Class.forName().
For example:

final Class activityX;
if (BuildConfig.CUSTOM_ACTIVITY_X) {
    try {
        activityX = Class.forName("CustomActivityX");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
} else {
    activityX = DefaultActivityX.class;
}

final Intent intent = new Intent();
...
intent.setClass(ThisActivity.this, activityX); 
startActivity(intent);

但是由于try/catch块,这显然会导致"activityX可能尚未初始化" .

However this obviously results in "activityX may not have been initialized" when you try to use it, because of the try/catch block.

如何克服这个问题?

推荐答案

所以这里有两个问题1)主要解决方法中的编码错误2)您要解决的更广泛的问题.

So there are two issues here 1) the coding bug in your main workaround 2) the broader problem you are trying to solve.

第一个问题比第二个问题可以提供更多帮助.您需要做的就是初始化变量.您问如何克服???"我相信这可以解决问题:

I can help more with the first issue than the second. All you need to do is initialize your variable. You asked "How can this be overcome???" I believe this will do the trick:

Class activityX = null;  //remove 'final' and initialize this to null, then null-check it later
if (BuildConfig.CUSTOM_ACTIVITY_X) {
    try {
        activityX = Class.forName("CustomActivityX");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
} else {
    activityX = DefaultActivityX.class;
}

final Intent intent = new Intent();
...
if(activityX != null) {
    intent.setClass(ThisActivity.this, activityX); 
    startActivity(intent);
}

现在,关于您要解决的更广泛的问题,很明显,上面的代码有一些代码味道,这表明可能有更好的方法.我使用了特定于风味的类,而不必将其复制到所有其他风味中.在那些情况下,其他类型的代码不会执行依赖于这些类的代码.例如,假设有一个付费"版本,其中免费"版本根本不会加载某些可供付费用户使用的类.

Now, regarding the broader issue you are solving, it's pretty clear that the code above has some code smells, which signal that there might be a better way. I've used flavor-specific classes without having to copy them to all the other flavors. In those cases, other flavors did not execute code that relied on those classes. For example, imagine a "paid" version where the "free" version simply never loads some of the classes available for paid users.

因此,我认为仅当所有口味都尝试加载所讨论的类时,才会出现此问题.在不了解您的整体代码库的情况下,很难提出替代方案.但是,我建议您尝试使用继承,抽象类或接口来解决您的问题.

So I think the issue only arises if all flavors attempt to load the class in question. It's hard to suggest an alternative without understanding your overall codebase. However, I would suggest that you try using inheritance, abstract classes, or interfaces to solve your problem.

我要调查的第一件事:活动是否真的是您需要覆盖的最小代码/行为单元?我怀疑不是.也许您可以创建一个包含所有样板代码的BaseActivity,然后将风味特定的代码隔离到需要它的确切组件中.

The first thing I would investigate: is the Activity really the smallest unit of code/behavior that you need to override? I suspect it isn't. Perhaps you can create a BaseActivity that has all the boilerplate code and then isolate the flavor-specific code into the exact components that require it.

例如,我使用的一种常见模式是通过XML文件处理这种事情.这样,活动和片段在各种口味中始终可以具有相同的 behavior ,但是它们会加载不同的布局.这些布局包含自定义视图组件(它们从公共接口或抽象父类扩展,从而允许活动将代码编码到该接口).现在,不需要某些类的调味料将永远不会尝试加载它们,因为它们不存在在由该特定调味料装载的布局中.

For example, a frequent pattern I've used is to handle this kind of thing via XML files. That way, activities & fragments can always have the same behavior across flavors but they load different layouts. Those layouts contain custom view components (who extend from a common interface or abstract parent class, which allows the activity to code to that interface). Now flavors that don't need certain classes will never attempt to load them because they don't exist in the layout that is loaded by that particular flavor.

无论如何,我希望这会有所帮助.如果不了解代码库的细微差别,很难解决更广泛的问题.我的建议是从根本上重新思考事情,并尝试使类加载器远离需要加载特定于口味的类的需求.

Anyway, I hope that helps in some way. It's very difficult to address your broader issue without understanding the nuances of your codebase. My advise would be to fundamentally rethink things and try to keep the class loader away from ever needing to load your flavor-specific classes.

这篇关于Gradle风格和main中重复类的解决方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆