IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> android 多产品项目搭建与变体的使用 -> 正文阅读

[移动开发]android 多产品项目搭建与变体的使用

????????由于公司产品的复杂性,连带着项目也跟着复杂起来,这时候也接触到了android的build 变体的使用。在这里记录一下这个过程,最初查看关于这一块的资料是在android的官网上看得,地址如下:配置 build 变体 ?|? Android 开发者 ?|? Android Developers

????????首先,我们公司是有多种产品的,每一种产品的类型有相同的也有不同的,而且种类很多,每一种产品下面还会继续细分不同的特性,例如有产品1、产品2、产品3....产品n,然后产品1可能会存在特性1、特性2......特性N,每个产品之间可能有相同部分,但是不相同的地方比较多,所以每一个产品可以作为一个单独的工程来实现,相同部分就抽成一个库进行依赖到项目中来,而每个产品下的特性则可以使用变体的方式来区分,当然不是说所有的特性都使用变体,如果特性增多,这个工程也会变得非常庞大的,还是要斟酌着使用。

? ? ? ? 针对上面所说的类型搭建项目,如下图所示:

? 接着,在公司的项目中,我主要使用到了以下几方面来划分产品与特性:

  1. build配置

????????当创建新模块时,Android Studio 会自动为您创建“debug”build 类型和“release”build 类型。虽然“debug”build 类型没有出现在 build 配置文件中,但 Android Studio 会使用?debuggable true?配置它,在项目app目录下的build.gradle中进行配置,配置的例子如下:

android {
    defaultConfig {
        
    }
    signingConfigs {	        
	  release {
	            storeFile file(appStoreFile)
                storePassword 'appStorePassword'
                keyAlias 'appKeyAlias'
                keyPassword 'appKeyPassword'
	        }
	
	  debug {

	            storeFile file(appStoreFile)
                storePassword 'appStorePassword'
                keyAlias 'appKeyAlias'
                keyPassword 'appKeyPassword'
	        }
    	}

    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
               // 配置debug版本的签名
               signingConfig signingConfigs.debug

	            // 是否开启代码混淆,默认false
	            minifyEnabled false

	            // 是否应该生成可调试的apk
	            debuggable true

	            // 混淆规则配置文件
	            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	            // 自定义buildType
	            buildConfigField 'String', 'BASE_URL', '"http://api-debug.**/"'
        }

        /**
         * The `initWith` property allows you to copy configurations from other build types,
         * then configure only the settings you want to change. This one copies the debug build
         * type, and then changes the manifest placeholder and application ID.
         */
        staging {
            initWith debug
            applicationIdSuffix ".debugStaging"
        }
    }
}

一般buildType中,我们会配置项目所需要使用到的环境变量,如测试环境地址和正式环境地址等,这个环境变量可以在代码中使用的,并且在编译打包的时候可以确定对应的值,?可以通过buildConfigField 关键字来进行配置,编译过后,我们可以通过BuildConfig.BASE_URL方式在代码中调用,如下图所示:

?获取到的值,就是在build.gradle中对应build type下配置的值。

配置完成后,sync同步一下,在gradle模块中可以看到配置的变化,当然如果点击Gradle出现Task list not build,没有Task出现,如下图?

?

?这是由于新版本的android studio默认设置了不同步task,我们可以点击Task list not build,去掉不同步的勾选,如下图:

?同步一下工程即可。我们可以在Gradle的Task-other看到这个变化,如下图:

也可以通过build variants切换当前工程的build type,切换到release下,那么buildConfig中的值也会跟着变化,如下图所示:

2.配置产品变种

????????如果我们的产品有多种特性类型,产品总体是一样,只有部分不一样,我们可以通过配置变体要区分特性与类型,例如,音箱项目分为蓝牙音箱、wifi音箱、蓝牙wifi音箱(只是举个例子),基本主体是一样的,只有连接方式不同,并且需要分别打不同的包,我们可以在app目录下的build.gradle配置type维度的变体,如下配置:

flavorDimensions "type"
    productFlavors {
        bluetooth {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension "type"
            //  这里配置apk的包名,配置之后的包名是com.zqy.app_project_one.bluetooth
            // 不需要修改包名直接不配置即可
            applicationIdSuffix ".bluetooth"
            versionNameSuffix "-bluetooth"
        }
        wifi {
            dimension "type"
            //  这里配置apk的包名,配置之后的包名是com.zqy.app_project_one.wifi
            applicationIdSuffix ".wifi"
            versionNameSuffix "-wifi"
        }
        bluetoothandwifi{
            dimension "type"
            //  这里配置apk的包名,配置之后的包名是com.zqy.app_project_one.bluetoothandwifi
            applicationIdSuffix ".bluetoothandwifi"
            versionNameSuffix "-bluetoothandwifi"
        }
    }

同步之后,可以在Gradle看到变体已经构建出来了:

?同样也可以通过build variants切换和查看当前工程的变体,如下:

?如果特性仅有打包的包名不一样,这样配置之后打出来的包就会带上对应变体设置的后缀名,但是如果代码也不一样的话,这就需要我们创建对应的变体模块来实现不一样的地方。

? ? ? ? 首先,我们可以右键项目,new一个java folder,选择需要创建的变体模块,如下:

创建之后,模块下面是什么都没有的,我们还需要创建目录,由于我当前项目是处于blueTooth变体模块,如下:

所以可以直接项目右键选择package,输入包名即可,如下:?

?如果当前工程不是处于想要创建变体的模式,则创建目录的时候,是没有package的选择的,只有Directory,如下:

?????????注意:main模块是所有变体都能访问到,变体模块下的代码是只有当前工程处于该变体模式下才能访问。

针对变体,一般我们有可能需要由以下的配置:

  • 每个变体app的启动入口不一样

????????每一个变体创建相同路径的MainActivity类,删除main目录下的MainActivity,统一通过main目录下的清单文件作为入口,这里创建demo和full两个变体,以这两个作为例子

app的build.gradle配置

flavorDimensions "version"
    productFlavors {
        demo {
            // Assigns this product flavor to the "version" flavor dimension.
            // If you are using only one dimension, this property is optional,
            // and the plugin automatically assigns all the module's flavors to
            // that dimension.
            dimension "version"
            //  这里配置apk的包名,配置之后的包名是com.zqy.app_project_one.bluetooth
            // 不需要修改包名直接不配置即可
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            dimension "version"
            //  这里配置apk的包名,配置之后的包名是com.zqy.app_project_one.wifi
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
  1. 将main模块下的MainActivity与activity_main.xml删除
  2. 变体模块下新增MainActivity与activity_main.xml

?

  1. 在main模块的清单文件设置程序的入口
<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Project">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

?通过build variants切换当前工程的build type,直接运行项目的代码,就可以调用对应变体下的MainActivity,当前是什么变体模式,就会使用变体目录下相同路径的类。

  • 更改默认源代码集配置

????????如果您的源代码未按照 Gradle 要求的默认源代码集文件结构进行组织(如上文关于创建源代码集的部分中所述),您可以使用?sourceSets?代码块更改 Gradle 为源代码集的每个组件收集文件的位置。sourceSets?代码块必须位于?android?代码块中。您无需改变源代码文件的位置,只需向 Gradle 提供相对于模块级?build.gradle?文件的路径,Gradle 应该会在该路径下找到每个源代码集组件的文件 。

 sourceSets{
        main{
            java.srcDirs = ['src/main/java']

            // If you list multiple directories, Gradle uses all of them to collect
            // sources. Because Gradle gives these directories equal priority, if
            // you define the same resource in more than one directory, you get an
            // error when merging resources. The default directory is 'src/main/res'.
            res.srcDirs = ['src/main/res']

            // Note: You should avoid specifying a directory which is a parent to one
            // or more other directories you specify. For example, avoid the following:
            // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
            // You should specify either only the root 'other/res1' directory, or only the
            // nested 'other/res1/layouts' and 'other/res1/strings' directories.

            // For each source set, you can specify only one Android manifest.
            // By default, Android Studio creates a manifest for your main source
            // set in the src/main/ directory.
            manifest.srcFile 'src/main/AndroidManifest.xml'
        }

        full{
            java.srcDirs = ['src/full/java']

            // If you list multiple directories, Gradle uses all of them to collect
            // sources. Because Gradle gives these directories equal priority, if
            // you define the same resource in more than one directory, you get an
            // error when merging resources. The default directory is 'src/main/res'.
            res.srcDirs = ['src/full/res']

            // Note: You should avoid specifying a directory which is a parent to one
            // or more other directories you specify. For example, avoid the following:
            // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
            // You should specify either only the root 'other/res1' directory, or only the
            // nested 'other/res1/layouts' and 'other/res1/strings' directories.

            // For each source set, you can specify only one Android manifest.
            // By default, Android Studio creates a manifest for your main source
            // set in the src/main/ directory.
            manifest.srcFile 'src/full/AndroidManifest.xml'
        }

        demo{
            java.srcDirs = ['src/demo/java']

            // If you list multiple directories, Gradle uses all of them to collect
            // sources. Because Gradle gives these directories equal priority, if
            // you define the same resource in more than one directory, you get an
            // error when merging resources. The default directory is 'src/main/res'.
            res.srcDirs = ['src/demo/res']

            // Note: You should avoid specifying a directory which is a parent to one
            // or more other directories you specify. For example, avoid the following:
            // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
            // You should specify either only the root 'other/res1' directory, or only the
            // nested 'other/res1/layouts' and 'other/res1/strings' directories.

            // For each source set, you can specify only one Android manifest.
            // By default, Android Studio creates a manifest for your main source
            // set in the src/main/ directory.
            manifest.srcFile 'src/demo/AndroidManifest.xml'
        }
    }
  • 库的build type不随主工程的build type变化而变化

????????有时候,我们项目主工程可能涉及到build type的新增,而主工程依赖了库工程,但是库工程并不需要新增这个build type类型,例如:主工程的build type如下:

 buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }

        debug {
            minifyEnabled false
            debuggable true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        
        }


        staging {
            initWith debug
            applicationIdSuffix ".debugStaging"
        }

依赖的库工程的build type配置如下:

  buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

?这时候如果当前工程为debug或release类型编译工程是不会有问题的,这是因为不管是工程还是库,android studio在创建这些工程和库的时候都会默认创建debug或release类型,但是一旦当前工程切换到staging 类型,此时编译运行工程,就会出现以下问题:

* What went wrong:
Could not determine the dependencies of task ':app-project-two:compileStagingJavaWithJavac'.
> Could not resolve all task dependencies for configuration ':app-project-two:stagingCompileClasspath'.
? ?> Could not resolve project :variants-res.
? ? ?Required by:
? ? ? ? ?project :app-project-two
? ? ? > No matching variant of project :variants-res was found. The consumer was configured to find an API of a component, preferably optimized for Android, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'staging', attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm' but:
? ? ? ? ? - Variant 'debugApiElements' capability Project:variants-res:unspecified declares an API of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm':
? ? ? ? ? ? ? - Incompatible because this component declares a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug' and the consumer needed a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'staging'
? ? ? ? ? ? ? - Other compatible attribute:
? ? ? ? ? ? ? ? ? - Doesn't say anything about its target Java environment (preferred optimized for Android)
? ? ? ? ? - Variant 'debugRuntimeElements' capability Project:variants-res:unspecified declares a runtime of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm':
? ? ? ? ? ? ? - Incompatible because this component declares a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'debug' and the consumer needed a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'staging'
? ? ? ? ? ? ? - Other compatible attribute:
? ? ? ? ? ? ? ? ? - Doesn't say anything about its target Java environment (preferred optimized for Android)
? ? ? ? ? - Variant 'releaseApiElements' capability Project:variants-res:unspecified declares an API of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm':
? ? ? ? ? ? ? - Incompatible because this component declares a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release' and the consumer needed a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'staging'
? ? ? ? ? ? ? - Other compatible attribute:
? ? ? ? ? ? ? ? ? - Doesn't say anything about its target Java environment (preferred optimized for Android)
? ? ? ? ? - Variant 'releaseRuntimeElements' capability Project:variants-res:unspecified declares a runtime of a component, as well as attribute 'org.jetbrains.kotlin.platform.type' with value 'androidJvm':
? ? ? ? ? ? ? - Incompatible because this component declares a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'release' and the consumer needed a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'staging'
? ? ? ? ? ? ? - Other compatible attribute:
? ? ? ? ? ? ? ? ? - Doesn't say anything about its target Java environment (preferred optimized for Android)
?

?这是由于依赖库和主工程的build type不一致的问题导致的,遇到这种问题,我们可以使用

matchingFallbacks 关键字来设置库的build type优先级,这也是android官方给出的解决方案,如下:

我们只需要在app的build.gradle的build type配置一下?matchingFallbacks即可,如下:

 buildTypes {
        release {
            .....
        }

        debug {
            .....
        }

       
        staging {
            ......
            // 关键的一行
            matchingFallbacks = ['debug','release']
        }
    }

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-06-29 19:12:58  更:2022-06-29 19:13:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年3日历 -2024/3/29 9:55:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码