gradle

Gradle的基本操作:配置同一应用不同的Application

2019-10-23  本文已影响0人  ag4kd

建议先看一下
Gradle的基本操作:AndroidManifest.xml中的meta-data标签、gradle中的manifestPlaceholder
一篇文章理解groovy中闭包closure的使用,不仅仅限于groovy的文章

源码

ApplicationId 与 PackageName

每一个Android应用都一个唯一的ID,这个ID在Android系统中是唯一的,成为应用程序的ID,就像一个人的身份证号一样,作为应用的唯一标识。

应用的ID在build.gradle脚本中配置,默认配置如下:

android {
    compileSdkVersion 28
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "command.ion.multiapps"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

在默认配置块defaultConfig中有一个默认的应用程序ID的配置:

applicationId "command.ion.multiapps"

defaultConfigandroid中的一个默认的配置块,用来定义一些默认配置。它是一个ProductFlavor,如果一个ProductFlavor没有为某些属性指定特定的配置的话,就会采用默认配置块中的默认配置。比如包名、版本号、版本名等。

通过查看源码,可以知道我们可以配置那些信息。

defaultConfig {
        applicationId "command.ion.multiapps"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

这一部分,其实是一个函数调用.

public void defaultConfig(Action<DefaultConfig> action) {
    checkWritability();
    action.execute(defaultConfig);
}

可以通过查看DefaultConfig这个类来得知可以配置那些信息。这个类位于comm.android.build.gradle.internal.dsl这个包下面。其原码为:

package command.android.build.gradle.internal.dsl;

import command.android.annotations.NonNull;
import command.android.build.gradle.internal.errors.DeprecationReporter;
import javax.inject.Inject;
import org.gradle.api.Project;
import org.gradle.api.logging.Logger;
import org.gradle.api.model.ObjectFactory;

/** DSL object for the defaultConfig object. */
@SuppressWarnings({"WeakerAccess", "unused"}) // Exposed in the DSL.
public class DefaultConfig extends BaseFlavor {
    @Inject
    public DefaultConfig(
            @NonNull String name,
            @NonNull Project project,
            @NonNull ObjectFactory objectFactory,
            @NonNull DeprecationReporter deprecationReporter,
            @NonNull Logger logger) {
        super(name, project, objectFactory, deprecationReporter, logger);
    }
}

有源码可知,其继承BaseFlavor,继续追踪源码,会在DefaultProductFlavor中发现我们需要的配置属性。

image.png

从图中可以用来配置不同ProductFlavor的资源的方法。

 /**
     * Adds a new generated resource.
     *
     * <p>This is equivalent to specifying a resource in res/values.
     *
     * <p>See <a
     * href="http://developer.android.com/guide/topics/resources/available-resources.html">Resource
     * Types</a>.
     *
     * @param type the type of the resource
     * @param name the name of the resource
     * @param value the value of the resource
     */
    public void resValue(@NonNull String type, @NonNull String name, @NonNull String value) {
        ClassField alreadyPresent = getResValues().get(name);
        if (alreadyPresent != null) {
            String flavorName = getName();
            if (BuilderConstants.MAIN.equals(flavorName)) {
                logger.info(
                        "DefaultConfig: resValue '{}' value is being replaced: {} -> {}",
                        name,
                        alreadyPresent.getValue(),
                        value);
            } else {
                logger.info(
                        "ProductFlavor({}): resValue '{}' value is being replaced: {} -> {}",
                        flavorName,
                        name,
                        alreadyPresent.getValue(),
                        value);
            }
        }
        addResValue(new ClassFieldImpl(type, name, value));
    }

查看上述实现接口,可以发现,这样一个接口

/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package command.android.builder.model;

import command.android.annotations.NonNull;
import command.android.annotations.Nullable;
import java.io.File;
import java.util.Collection;
import java.util.Map;

/**
 * Base config object for Build Type and Product flavor.
 */
public interface BaseConfig {

    @NonNull
    String getName();

    /**
     * Returns the application id suffix applied to this base config.
     * To get the final application id, use {@link AndroidArtifact#getApplicationId()}.
     *
     * @return the application id
     */
    @Nullable
    String getApplicationIdSuffix();

    /**
     * Returns the version name suffix of this flavor or null if none have been set.
     * This is only the value set on this product flavor, not necessarily the actual
     * version name suffix used.
     *
     * @return the version name suffix, or {@code null} if not specified
     */
    @Nullable
    String getVersionNameSuffix();

    /**
     * Map of Build Config Fields where the key is the field name.
     *
     * @return a non-null map of class fields (possibly empty).
     */
    @NonNull
    Map<String, ClassField> getBuildConfigFields();

    /**
     * Map of generated res values where the key is the res name.
     *
     * @return a non-null map of class fields (possibly empty).
     */
    @NonNull
    Map<String, ClassField> getResValues();

    /**
     * Specifies the ProGuard configuration files that the plugin should use.
     *
     * <p>There are two ProGuard rules files that ship with the Android plugin and are used by
     * default:
     *
     * <ul>
     *   <li>proguard-android.txt
     *   <li>proguard-android-optimize.txt
     * </ul>
     *
     * <p><code>proguard-android-optimize.txt</code> is identical to <code>proguard-android.txt
     * </code>, exccept with optimizations enabled. You can use <code>
     * getDefaultProguardFile(String filename)</code> to return the full path of the files.
     *
     * @return a non-null collection of files.
     * @see #getTestProguardFiles()
     */
    @NonNull
    Collection<File> getProguardFiles();

    /**
     * Returns the collection of proguard rule files for consumers of the library to use.
     *
     * @return a non-null collection of files.
     */
    @NonNull
    Collection<File> getConsumerProguardFiles();

    /**
     * Returns the collection of proguard rule files to use for the test APK.
     *
     * @return a non-null collection of files.
     */
    @NonNull
    Collection<File> getTestProguardFiles();

    /**
     * Returns the map of key value pairs for placeholder substitution in the android manifest file.
     *
     * This map will be used by the manifest merger.
     * @return the map of key value pairs.
     */
    @NonNull
    Map<String, Object> getManifestPlaceholders();

    /**
     * Returns whether multi-dex is enabled.
     *
     * This can be null if the flag is not set, in which case the default value is used.
     */
    @Nullable
    Boolean getMultiDexEnabled();

    @Nullable
    File getMultiDexKeepFile();

    @Nullable
    File getMultiDexKeepProguard();
}

其中就有我们需要的信息。

/**
 * Returns the application id suffix applied to this base config.
 * To get the final application id, use {@link AndroidArtifact#getApplicationId()}.
 *
 * @return the application id
 */
@Nullable
String getApplicationIdSuffix();

这样我们可以再不同的ProductFlavor中配置不同applicationIdSuffix,从而得到不同的应用ID了。

前面说过android就是productFlavor,那么在其闭包中可以调用

    /**
     * Encapsulates all product flavors configurations for this project.
     *
     * <p>For more information about the properties you can configure in this block, see {@link
     * ProductFlavor}
     */
    public void productFlavors(Action<? super NamedDomainObjectContainer<ProductFlavor>> action) {
        checkWritability();
        action.execute(productFlavors);
    }

来实现不同productFlavor的属性配置


    // Specifies the flavor dimensions you want to use. The order in which you
    // list each dimension determines its priority, from highest to lowest,
    // when Gradle merges variant sources and configurations. You must assign
    // each product flavor you configure to one of the flavor dimensions.

    flavorDimensions 'shanghai', 'beijing'// 定义 dimensions,这是一列表,顺序表示优先级
    productFlavors{
        //配不同的 productFlavor
        xiaomi{
            dimension 'shanghai'//每一个flavor都需要一个 Dimension
        }
        huawei{
            dimension 'shanghai'
        }
        apple{
            dimension 'beijing'
        }
    }

我这里使用一个dimension

flavorDimensions 'shanghai'// 定义 dimensions,这是一列表,顺序表示优先级
productFlavors{
    //配不同的 productFlavor
    xiaomi{
        dimension 'shanghai'//每一个flavor都需要一个 Dimension
    }
    huawei{
        dimension 'shanghai'
    }
    apple{
        dimension 'shanghai'
    }
}

然后在为其配置不同的applicationIdSuffix,就可以在同一Android设备中安装三个相同应用了。

前面分析可知,还可以在这里配置不同资源,那么来改变一下应用的名字吧;有两种方式,一是通过占位符,关于占位符的讲解,二是通过前面说的resValue(@NonNull String type, @NonNull String name, @NonNull String value)

 resValue "string", "app_name", ""

这种方式配置资源ID,可以再java代码中使用,比如为不同的产品配置不同的接口域名,不如测试环境还是开发环境

productFlavors{
        //配不同的 productFlavor
        xiaomi{
            dimension 'shanghai'//每一个flavor都需要一个 Dimension
            applicationIdSuffix 'first'//配置不同的应用id
            resValue "string", "app_name", "first"//配置不同的应用的名字
            resValue "string", "host", "http://www.baidu.com"//配置不同的服务器接口
        }
        huawei{
            dimension 'shanghai'
            applicationIdSuffix 'second'
            resValue "string", "app_name", "second"
            resValue "string", "host", "http://www.google.com"
        }
        apple{
            dimension 'shanghai'
            applicationIdSuffix 'third'
            resValue "string", "app_name", "third"
            resValue "string", "host", "first"
            resValue "string", "host", "http://www.souhu.com"
        }
    }

名字的使用方式跟strings.xml中的属性的使用方式是一样的。

android:label="@string/app_name"

配置不同名字,如果脚本中的属性名字与xml中的冲突,删除xml中的名字,因为这个时候我们用的是脚本中的名字。

TextView textView = findViewById(R.id.host);
textView.setText(getString(R.string.host));

配置完成后,在不同的构建变体中切换不同变体,就可以将它们同时安装到同一台Android手机上了。

image.png

如果AndroidManifest.xml中有provider等问题,可以用过占位符来解决。关于占位符的讲解

上一篇下一篇

猜你喜欢

热点阅读