如何在产品风味中使用不同的启动器 activity?

How to use a different launcher activity in a product flavor?

我正在开发一个 Android 库项目,在默认 src/main/AndroidManifest.xml 中,MainActivity 是启动器 activity.

为了其他事情,我创建了产品口味。是的,如果我想在不同的产品口味中触发/显示不同的活动,它就完美了。但是,我想保留 默认启动器 activity 来自 src/main/ 文件夹,同时注册另一个风味 activity 作为新的启动器 activity。因此,对于不同的产品口味,我可以有不同的启动器活动,并且从它们我仍然可以在 src/main/.

中开始原始的 "launcher" activity

谁能告诉我如何实现它?非常感谢。

备注:

  1. 不推荐将 if (BuildConfig.FLAVOR.equals("flavorName")) 代码添加到原始启动器 activity。因为我不想修改别人的生产代码(这是一个库项目)。

  2. 我试过 manifestmergertools:replace,但似乎对 intent-filter 不起作用(我注意到 intent 的元素合并策略-过滤器总是)。

<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />

如果这可行,能否请您指导我如何使它工作?谢谢。

我尝试过的:

  1. 启用清单合并,这不起作用;
  2. 使用 activity-alias,这也不起作用。

最后发现加一行就可以解决问题了:

<category android:name="android.intent.category.DEFAULT" />

============================================= =====

为了说清楚,我将再过一遍问题和解决方案:

src/main/java下面有一个MainActivity,在对应的src/main/AndroidManifest.xml里面指定了MainActivity作为启动器activity:

<activity android:name=".MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

这是一个非常简单的部分。现在我们从产品风味部分开始。

由于某种原因,在产品风味中,我不想覆盖 MainActivity,而是有一个 YetAnotherMainActivity。目标是 YetAnotherMainActivity 设置为产品风味中的新启动器 activity,它应该仍然可以调用 MainActivity

在这里,您可以将产品风味中的新 activity 设置为新启动器 activity:

flavorX/AndroidManifest.xml:

<activity android:name="com.example.YetAnotherMainActivity"
    android:label="@string/title_yet_another_main_activity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

是的,事实证明这非常容易。只需添加 android.intent.category.DEFAULT.

最简单和最干净的解决方案是只保留一个 Manifest 并编写两个不同的 MainActivity.java class 每个 flavor 一个,以避免清单节点重复。

在 gradle

中给出了两种口味
productFlavors {
        paid {
            packageName "com.example"
        }

        demo {
            packageName "com.example.demo"
        }
    }

鉴于此项目结构

app/
|--libs/
|--src/
   |--paid/
   |  |--java/
   |     |--com/example/
   |        |--MainActivity.java
   |--demo/
   |  |--java/
   |     |--com/example/
   |        |--MainActivity.java
   |--main/
      |--java/
      |  |--...
      |--res/
      |  |--...
      |--AndroidManifest.xml

还有这个Android清单

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.flavors">

    <application
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme">

        <activity
                android:name=".MainActivity"
                android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

    </application>

</manifest>

最简单的工作解决方案是使用清单合并并使用

<intent-filter tools:node="removeAll">  

如下所示 post :

Merging android manifest files, conflicting filter

我认为 <activity-alias> 比任何其他解决方案更适合那里(不知道为什么 @JingLi 无法让它工作。也许一年前有一些麻烦,但现在没关系)。

例如,我们在 main 中有以下清单:

<manifest
        xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.application">
    <application>
        <activity android:name=".InfoActivity"/>
        <activity-alias 
            android:name="com.example.application.Launcher"
            android:targetActivity=".InfoActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity-alias>
    </application>
</manifest>

我们想用 debug 风格的 DebugInfoActivity 替换启动器 activity。所以,我们只需要替换指定的 <activity-alias> 标签中的 targetActivity 属性:

<manifest 
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
    <application>
        <activity android:name=".DebugInfoActivity"/>
        <!-- to not litter the manifest -->
        <activity 
                android:name="com.example.application.InfoActivity"
                tools:node="remove"/>
        <activity-alias
                android:name="com.example.application.Launcher"
                android:targetActivity=".DebugInfoActivity"
                tools:replace="android:targetActivity"/>
    </application>
</manifest>

备注:

  • 在示例中,我们为 maindebug 使用相同的包名称。
  • 我们必须输入activity-别名的全名,这样合并才能正确合并它们。

通过解决方案,我们还可以从 main activity 别名继承所有属性和子项,而不是在 debug.

中重复它们

根据您的风格创建一个不同的 AndroidManifest.xml 文件。然后将您的 DifferentFlavorMainActivity.java 设置为启动器 activity,全名如下:

android:name="com.android.application.paid.MainActivity"

我想我没有迟到:) 所以今天我遇到了同样的问题。 @seroperson 解决方案是正确的,但如果您根本不需要默认启动器 activity,则只需在您的口味清单中使用以下代码:

<activity
        android:name=".DefaultLauncherActivity"
        tools:node="remove"
        >

Android 合并正在将 intent-filter 从主清单 lancher 合并到 flavors。我还没有找到防止这种情况发生的方法。您最终在设备上有 2 个应用程序图标(每个用于启动器 Activity)。

基于此,您无法完全覆盖主清单中的设置。 Solition 可能是在主文件夹中仅保留 shell 的清单并在每个版本中实现清单,或者从主文件夹中删除冲突活动并在每个版本中独立实现。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.adamassistant.app">

    <!-- empty shell, implementation in flavors folders -->

</manifest>