美文网首页gradle
Gradle的基本操作:配置同一应用不同的Application

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

作者: ag4kd | 来源:发表于2019-10-23 16:29 被阅读0次

    建议先看一下
    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等问题,可以用过占位符来解决。关于占位符的讲解

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/vmpkvctx.html