如何从主目录访问产品风味的类?

5
我的应用程序中有两种产品味道,即flavorOne(src/flavorOne/java)flavorTwo(src/flavorTwo/java)Main(src/main/java)是一个目录,其中两种风味都使用类。我想从src/main/java/ActivityA中的活动开始src/flavorTwo/java/ActivityB.java的活动。在运行flavorTwo时它可以工作,但是当我切换到flavorOne时,它显示import com.packagename.ActivityB错误。
+ App // module
    |- src
       |- main// shared srcDir
          |- java
           |- SharedActivity
       + flavorOne
          |- java
           |- FlavorOneActivity
       + flavorTwo
          |- java
           |- FlavorTwoActivity

这里是SharedActivity.java,位于src/main/java/SharedActivity.java目录下。

package com.example.buildvariants;

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
/**********************  works if it is in flavorOne otherwise it shows error on this package import ***********/
import com.example.buildvariants.flavorOne.LoginActivity;

public class SharedActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        //if flavor is flavorTwo hide the fab
        //else flavor is flavorOne show fab and launch activity under flavorOne/java/LoginActivity.java
        if (BuildConfig.FLAVOR.equalsIgnoreCase("flavorTwo")) {
            fab.setVisibility(View.GONE);
        } else {
            fab.setVisibility(View.VISIBLE);
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                            .setAction("Action", null).show();
                    startActivity(new Intent(SharedActivity.this, LoginActivity.class));

                }
            });
        }
    }
}

flavorOne下的Activity,路径为src/flavorOne/FlavorOneMainActivity.java。

package com.example.buildvariants.flavorOne;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.example.buildvariants.R;
import com.example.buildvariants.SharedActivity;

public class FlavorOneMainActivity extends AppCompatActivity {

    private static final String TAG =FlavorOneMainActivity.class.getSimpleName() ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.flavor_one_activity_main);
        Intent intent = new Intent(FlavorOneMainActivity.this, SharedActivity.class);
        startActivity(intent);
    }
}

flavorTwo下的Activity,位于src/flavorOne/FlavorTwoMainActivity.java文件中。

package com.example.buildvariants.flavorTwo;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.example.buildvariants.R;
import com.example.buildvariants.SharedActivity;

public class FlavorTwoMainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.flavor_two_activity_main);
        startActivity(new Intent(FlavorTwoMainActivity.this, SharedActivity.class));
    }
}

当我更改构建变体flavorTwo时,在SharedActivity(src/main/java/)导入包时出现如下错误

package com.example.buildvariants;

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
/********error  on package import***********/
import com.example.buildvariants.flavorOne.LoginActivity;

public class SharedActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);

        //if flavor is flavorTwo hide the fab
        //else flavor is flavorOne show fab and launch activity under flavorOne/java/LoginActivity.java
        if (BuildConfig.FLAVOR.equalsIgnoreCase("flavorTwo")) {
            fab.setVisibility(View.GONE);
        } else {
            fab.setVisibility(View.VISIBLE);
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                            .setAction("Action", null).show();
                    startActivity(new Intent(SharedActivity.this, LoginActivity.class));

                }
            });
        }
    }
}

什么是这个问题的最佳解决方案?

当您想要访问一个在包外的类时,您必须导入该包。例如,您是否已经在falvorOne/java中导入了flavourTwo? - JBALI
@user3624028,您能否请提供一个示例,说明如何在flavorOne/java中导入flavorTwo? - c__c
为了试用,请尝试输入:import flavorOne.java; - JBALI
@user3624028 我已经导入了那个类,但当我将构建变体更改为另一种风味时,在该导入处显示错误。 - c__c
请添加您的代码片段。 - JBALI
@user3624028,我已更新代码片段。 - c__c
2个回答

6

它失败了,因为在构建应用程序时,每次只存在一个产品风味的代码。因此,你真正想要做的是在两个产品风味中使用单个类名。

假设我们有一个单一的类需要根据不同的产品风味进行替换,我们称之为ReplacableActivity.java

为了使替换工作,两个产品风味都需要具有这个类,并且主要源集不会有这个类。

例子:

src/main/com/blah/myApp/ReplacableActivity #<- should not exist
# exists and is the implementation of ReplacableActivity for `flavorOne`
src/flavorOne/com/blah/myApp/ReplacableActivity.java
# exists and is the implementation of ReplaceableActivity.java for `flavorTwo`
src/flavorTwo/com/blah/myApp/ReplacableActivity.java

现在对于你正在构建的所有产品风格,ReplacableActivity 存在并可以从 main 源集中引用。在构建时,只会将该特定风格的 ReplaceableActivity 打包到应用中。现在,你的导入将按预期工作:import com.blah.myApp.ReplaceableActivity; 来自 main 源集。
编辑:
如果你只关心隐藏或显示一个单独的元素,那么上述方法有些过于复杂了。从 BuildConfigField 中获取它会更容易。
android {
    productFlavors {
        flavorOne {
            buildConfigField "boolean", "flavorShowsFab", 'false'
        }
        flavorTwo {
            buildConfigField 'boolean', 'flavorShowsFab', 'true'
        }
    }

然后在你的Java代码中只需执行以下操作:
findViewById(R.id.myHidableFab).setVisibility(BuildConfig.flavorShowsFab ? View.VISIBLE : View.GONE));

在我的情况下,两种版本都有相同的用户界面,但是它们的区别在于一个版本包含浮动操作按钮,而另一个版本则没有。我想在SecondFlavor中从该浮动操作按钮启动活动。在第一个版本中,我通过检查构建变体来隐藏浮动操作按钮。 - c__c
更新答案以展示基于productFlavors隐藏视图的更简单方法。 - JBirdVegas
谢谢你的解决方案,这是唯一的方法吗?在我的情况下,我将ReplacableActivity保留为主要活动,因为它需要两种口味和相同的代码。如果构建是第一个口味,那么我想在浮动操作按钮的操作中打开第一个口味中存在的活动,但不适用于第二个口味。如果除了FAB的操作之外,两者的代码都相同,是否重复在两个口味中编写代码是可行的? - c__c
Gradle非常灵活,因此您可以以多种方式实现它。 话虽如此,重复代码从来都不是好事。 如果您需要使两个版本的代码相同,除了一个决策点之外,则使用BuildConfig值进行if(BuildConfig.blah){} else {}最有意义... 您不想维护两份相同的代码。 如果在重复的代码中发现错误/功能怎么办? 现在您必须更新两个位置而不是一个。 - JBirdVegas

1

通过使用继承概念和在gradlemanifest文件中进行少量更改,而不是为每个风味创建不同的代码,我们可以维护相同的代码,我们可以通过以下三个步骤实现:

1) 在应用程序级别的build.gradle文件中合并每个风味的源集

sourceSets {
         flavorOne {
            java.srcDirs = ['src/main/java','src/flavorOne/java']
        }
    flavorTwo {
            java.srcDirs = ['src/main/java','src/flavorTwo/java']
        }
    }

2)主要的技巧在这里发生,为每个口味创建清单,其中定义了启动器活动,但不在主要口味中。
在主清单文件中:
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.myapplication">

        <application

    //no launcher defined here

        </application>

    </manifest>

在一个味道中的主要清单文件:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.myapplication">

    <application>
        <activity
            android:name="{Your launcher activity}">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

在第二个清单文件中:

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.example.myapplication">

        <application>
            <activity
                android:name="{Your launcher activity}">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />

                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>

    </manifest>

为了在构建gradle时合并清单文件时不出现错误,
3)因此,在主java src中,创建BaseSharedActivity.java并编写您的正常实现,在每个flavor中创建SharedActivity.java,并从每个flavor中定义的启动器活动调用它们。
在每个flavor的SharedActivity.java中:
public class SharedActivity extends BaseSharedActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
//You can override whatever method you want from BaseSharedActivity here 
}

这样,您就不需要为SharedActivity添加相同的代码,可以在不同的味道中为同一屏幕提供最大的自定义功能,您也可以对启动器活动使用相同的逻辑。

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