最近frameWork的blog都没有写了,原因就在于要重新搭一个APP的架子出来,需要无脑的搬砖。
还是说选型吧,viewModel是首选方案,语言上直接就是全kotlin,组件化技术用DD的DRouter,虽然那个是Java 写的,但是不自己写KSP的情况下,感觉他是最好的方案了,当然功能也蛮多的,啥都能找到,kapt还是基于apt封装,感觉上还不如直接APT,因为compose和xml 这种方式可以混合开发。所以把能整的都整一下。
gradle相关的
我们先直接基于AS创建一个app,先不选择compose,就是原始的那种xml 风格即可,gradle的DSL语言还是选择kotlin DSL,因为本地是7+,所以我们手动把gradle 版本修改到8以下,通过Android build tools 版本也和gradle 版本有一定约束关系(参考),主要是原因还是看国内的组件化插件或者编译时插件到底支持到了什么版本。 gradle版本:
ini
distributionUrl=https://services.gradle.org/distributions/gradle-7.5-all.zip
Android与kotlin plugin 版本
bash
plugins {
id("com.android.application") version "7.4.2" apply false
id("com.android.library") version "7.4.2" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
}
可以看到,这个和之前的classpath 还是不一样,但是还是可以使用classpath,这个后面我们使用DRouter的时候再说。这个可以简单的理解成 Android build tools 版本迭代导致的,如果说改到4.2.2,那么就会发现很多东西报错,所以还是不往太低的改。
设置maven的地址和plugin 的地址
这个玩意被修改到了setting.gradle 里面去了
scss
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
maven { setUrl("https://s01.oss.sonatype.org/content/repositories/releases/") }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
maven { setUrl("https://s01.oss.sonatype.org/content/repositories/releases/") }
maven { setUrl("https://jitpack.io") }
maven { setUrl("https://maven.aliyun.com/repository/public/") }
mavenCentral()
}
}
这个就说gradle 迭代的问题了。
配置Androidx 与 androidx 代码都自动转换
在gradle.propertis 文件里面加入:
ini
android.useAndroidX=true
android.enableJetifier=true
第二行:启用 Jetifier 工具,该工具可以自动将你的项目中使用的第三方库迁移到 AndroidX。
buildSrc 本地化gradle plugin
这个熟悉编译时技术的同学可能有经验,这个玩意里面的plugin 可以不通过classpath 导入就可以使用,而且是默认加载。所以我们先在根目录创建一个这样的文件夹,至于为什么是这个名字,gradle人家就定义成这样了,只有写成这样才会默认加载,不是这样要手动添加clsspath。
无论是plugins 还是classpath 的目标都是为gradle 工程服务的,所以我们可以在gradle中直接使用。我们先在buildSrc 目录下创建一个:build.gradle.kts 文件。然后导入相应的配置:
ini
plugins {
`java-library`
`kotlin-dsl`
}
repositories {
mavenCentral()
google()
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
dependencies {
}
因为这个buildSrc 的src 目录中的的所有class 都可以在整个gradle 工程中直接访问,那么常用的方式就有:
- 定义 maven
- 定义app 的配置信息中的Flavor。
- 定义plugin
- 定义一些自定义的task,比如说打包的时候,拉预置数据,打包完成后自动上传到某些服务器上面去,或者说切换工作空间什么的。
常规配置
因为我们要考虑到是组件化开发,一些UImodule的配置就几乎是固定的,而gradle 又允许导入外部的gradle 文件,所以我们可以单独写一个gradle文件,用于配置一些UI层的配置,比如说,我们创建一个叫common.gradle的文件在项目的根目录,
至于为啥上面用kotlin 的DSL,但是这个文件要用groovy的DSL,因为我还没有解决通过apply from 导入到gradle文件 Android 闭包找不到的问题,但是groovy的DSL没有这个问题。
arduino
// 这个玩意写成kotlin dsl 会报Android 闭包找不到。先用groovy 写。等找到原因了再全部切换到kotlin dsl
// 这个玩意里面不能导入plugin。
android{
compileSdk AndroidConfig.compileSdk
defaultConfig{
minSdk AndroidConfig.minSdk
targetSdk AndroidConfig.targetSdk
versionCode 1
versionName "1.0"
}
kotlinOptions {
jvmTarget = BuildConfig.kotlinOptionsJvmTarget
}
compileOptions {
sourceCompatibility BuildConfig.sourceCompatibility
targetCompatibility BuildConfig.targetCompatibility
}
buildFeatures {
viewBinding true
aidl true
buildConfig true
// 开启compose
compose true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.3"
}
lint {
abortOnError false
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
signingConfig signingConfigs.debug
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation Common.ktx
implementation Common.appcompat
implementation Common.material
implementation Common.constraintlayout
implementation Common.kotlinx_coroutines_android
implementation Common.lifecycle_common
implementation Common.lifecycle_viewmodel_compose
implementation Common.lifecycle_livedata_core_ktx
implementation Common.activity_ktx
implementation Common.fragment_ktx
// 路由
implementation Router.router
implementation Router.page
implementation Router.process
}
通过上面的代码,可以看到,我们使用了在buildSrc 中定义的常量,以Common 为例:
ini
object Common{
const val ktx="androidx.core:core-ktx:1.8.0"
const val appcompat="androidx.appcompat:appcompat:1.6.1"
const val material="com.google.android.material:material:1.5.0"
const val constraintlayout="androidx.constraintlayout:constraintlayout:2.1.4"
const val kotlinx_coroutines_android="org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
const val lifecycle_common="androidx.lifecycle:lifecycle-common:2.6.1"
const val lifecycle_viewmodel_compose="androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1"
const val lifecycle_livedata_core_ktx="androidx.lifecycle:lifecycle-livedata-core-ktx:2.6.1"
const val activity_ktx="androidx.activity:activity-ktx:1.7.2"
const val fragment_ktx="androidx.fragment:fragment-ktx:1.6.1"
}
同时还可以发下一个问题,这个gradle 文件中没有任何的plugin 或者apply 的代码,可以简单理解成kotlin 的inline 函数的效果,通过下列的代码导入common.gradle
ini
apply(from = "../common.gradle")
Android lib module的配置
因为我们写了一个common.gradle,所以我们lib 的代码就很简单了:
python
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
apply(from = "../common.gradle")
android {
namespace = "com.luoye.baseui"
}
dependencies {
api(project(mapOf("path" to ":common")))
}
module 中的差异部分也可以自己配置。
Drouter 的配置
为什么要先写这个,因为Drouter 的plugin 只需要写到application里面即可DRouter gitHub地址,可以在demo 代码里面看到,他依旧是使用classpath 导入plugin的,那么能不能通过plugins{id } 这种方式获得呢?
反正我是没有弄出来,没有搞明白plugins{id} 怎么拼drouter-plugin 这一部分。
既然plugins没有整通畅,那么还是使用classpath 即可:
scss
buildscript{
repositories {
google()
mavenCentral()
gradlePluginPortal()
maven { setUrl("https://s01.oss.sonatype.org/content/repositories/releases/") }
}
dependencies{
classpath("io.github.didi:drouter-plugin:1.3.4")
}
}
可以看到,我们classpath 没有写Android build tools,因为我们上面plugins 里面已经申请了。
app的配置
整了这么多,终于写到了app 这边了。我们app依旧导入common.gradle 文件,至于common.gradle 是否臃肿,臃肿了就拆分成多个即可,我们app 主要是对打包进行限制,比如说NDK,比如说排除某些maven版本,比如说flavor。
kotlin
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.didi.drouter")
}
apply(from = "../common.gradle")
drouter {
debug = true
}
android {
namespace = "com.example.luoye"
applicationVariants.all {
outputs.filterIsInstance<com.android.build.gradle.internal.api.ApkVariantOutputImpl>().forEach {
it.outputFileName = "${applicationId}_v${versionName}_${versionCode}_${buildType.name}.apk"
}
}
// 配置 productFlavors
flavorDimensions += AndroidConfig.DIMENSION_ENV
productFlavors {
arrayOf(AndroidConfig.FLAVOR_MOBILE_PRD, AndroidConfig.FLAVOR_MOBILE_DEV).forEach {
create(it) {
dimension = AndroidConfig.DIMENSION_ENV
val config= AndroidConfig.getConfigInfo(it)
applicationId = config.applicationId
versionName = project.properties["AppVersionName"].toString().trim().ifEmpty { config.versionName }
versionCode = project.properties["AppVersionCode"].toString().trim().toIntOrNull() ?: config.versionCode
// 调试模式通过打包进行配置。
val appDebug=project.properties["app_debug"].toString().toBoolean()
buildConfigField("Boolean","app_debug","$appDebug")
}
}
}
packagingOptions {
jniLibs {
keepDebugSymbols += "*/armeabi-v7a/*.so"
}
// compose 配置
resources {
excludes+="/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
// 内存泄漏检测
debugImplementation(Debug.leakcanary)
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar", "*.aar"))))
}
可以看到下面代码用于配置apk 文件的文件名。
kotlin
applicationVariants.all {
outputs.filterIsInstance<com.android.build.gradle.internal.api.ApkVariantOutputImpl>().forEach {
it.outputFileName = "${applicationId}_v${versionName}_${versionCode}_${buildType.name}.apk"
}
}
而且我们还通过 project.properties["AppVersionName"]
获取了gradle.properties 中的AppVersionName,当我们工程需要使用Jenkins打包的时候,往往就是在这个文件里面配置东西,然后将数据通过gradle的执行设置到APP的配置文件里面,比如说string,清单文件,buildConfig 文件等等。
结束
其实这篇蛮简单的,主要是思路的和一些常规的经验,用于提高后续的编码速率,也没有啥相关的知识点。