Androidプラットフォーム - パート13: "純粋JVM"モジュールの追加

🌱 ブランチ: 13/jvm-only-modules

🔗 リポジトリ: github.com/rsicarelli/kotlin-gradle-android-platform

⬅️ 前の記事: パート12: Androidライブラリのビルド時間の最適化

➡️ 次の記事: パート14: Kotlinコンパイラの実験的な機能を活用する


前回の記事では、Android Gradle Plugin(AGP)のさまざまな機能を無効にすることで、Androidモジュールのコンパイルを最適化しました。

今回の記事では、「純粋JVM」モジュール(java-library)と「Androidライブラリ」モジュール(com.android.library)の違いについて話します。また、これらの機能をサポートするためにプラットフォームを拡張します。


純粋JVMモジュールとは何か?

純粋JVMモジュールは、Java Virtual Machine (JVM)のみを使用して実行されるモジュールです。つまり、Androidと直接関連または依存していません。

簡単に言えば、Androidモジュールの特有の複雑さがない純粋なJavaモジュールです。

その結果、コンパイルが効率的になります。なぜなら、これらのモジュールはAGPのコンパイルプロセスを通じていないからです。

純粋JVMモジュールを使うシチュエーション

  1. ビジネスロジック: Androidに直接依存しない計算、バリデーション、リストの操作などのビジネスロジックに関連するコードに。

  2. 汎用ライブラリ: Androidプロジェクトと純粋なKotlin/JVMプロジェクトの両方で使用することができるライブラリを開発している場合に。

  3. コアモジュール: データベース、ネットワーク、ログ記録に関連するモジュールは、純粋なKotlin/JVMで構築できます。

純粋JVMモジュールを受け入れるためにプラットフォームを装飾する

新しいエントリーポイントとして jvmLibrary() を追加し、それを applyJvmLibrary() で装飾する提案です。

1 - kotlin.ktファイルに fun Project.applyJvmLibrary関数を作成します。

import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

internal fun Project.applyJvmLibrary() {
    pluginManager.apply("java-library")
    extensions.configure<JavaPluginExtension> {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }

    applyKotlinOptions()
}

internal fun Project.applyKotlinOptions() {
    tasks.withType<KotlinCompile>().configureEach {
        kotlinOptions {
            jvmTarget = "17"
        }
    }
}

2 - これからさまざまなカスタマイズを適用するために、モジュールが個別にコンパイルを設定できるようにします。Options をそのままにしないために build-logic/src/../options フォルダを作成し、AndroidOptions.kt をそこへ移動します。

CompilationsOptions クラスを作成し、内部を宣言します。

import org.gradle.api.JavaVersion

internal data class CompilationOptions(
    val javaVersion: JavaVersion,
    val jvmTarget: String,
    val allWarningsAsErrors: Boolean,
)

class CompilationOptionsBuilder {

    var javaVersion: JavaVersion = JavaVersion.VERSION_17
    var jvmTarget: String = "17"
    var allWarningsAsErrors: Boolean = false

    internal fun build(): CompilationOptions = CompilationOptions(
        javaVersion = javaVersion,
        jvmTarget = jvmTarget,
        allWarningsAsErrors = allWarningsAsErrors
    )
}

3 - applyJvmLibrary() 関数を CompilationOptions を受け取るように調整します。

import com.rsicarelli.kplatform.options.CompilationOptions
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

internal fun Project.applyJvmLibrary(compilationOptions: CompilationOptions) {
    pluginManager.apply("java-library")
    applyJavaCompatibility(compilationOptions.javaVersion)
    applyKotlinOptions(compilationOptions)
}

internal fun Project.applyKotlinOptions(compilationOptions: CompilationOptions) {
    tasks.withType<KotlinCompile>().configureEach {
        kotlinOptions {
            allWarningsAsErrors = compilationOptions.allWarningsAsErrors
            jvmTarget = compilationOptions.jvmTarget
        }
    }
}

private fun Project.applyJavaCompatibility(javaVersion: JavaVersion) {
    extensions.configure<JavaPluginExtension> {
        sourceCompatibility = javaVersion
        targetCompatibility = javaVersion
    }
}

4 - 関数 applyKotlinOptions のシグネチャを変更し、共通の android.kt デコレーション内で共有していたのを修正します。

internal fun Project.applyAndroidApp(
    androidAppOptions: AndroidAppOptions,
    compilationOptions: CompilationOptions,
) {
    applyAndroidCommon(
        androidOptions = androidAppOptions,
        compilationOptions = compilationOptions
    )
    // ...
}

internal fun Project.applyAndroidLibrary(
    androidLibraryOptions: AndroidLibraryOptions,
    compilationOptions: CompilationOptions,
) {
    applyAndroidCommon(
        androidOptions = androidLibraryOptions,
        compilationOptions = compilationOptions
    )
    // ...
}

private fun Project.applyAndroidCommon(
    androidOptions: AndroidOptions,
    compilationOptions: CompilationOptions,
) =
    with(commonExtension) {
        // ...

        compileOptions {
            sourceCompatibility = androidOptions.javaVersion
            targetCompatibility = androidOptions.javaVersion
        }

        applyKotlinOptions(compilationOptions)
        // ...
    }

新しいAPIを公開する

KPlatformPlugin.kt を新しい定義で更新します。

fun Project.androidApp(
    compilationOptionsBuilder: CompilationOptionsBuilder.() -> Unit = { },
    appOptionsBuilder: AndroidAppOptionsBuilder.() -> Unit = { },
) = applyAndroidApp(
    androidAppOptions = AndroidAppOptionsBuilder().apply(appOptionsBuilder).build(),
    compilationOptions = CompilationOptionsBuilder().apply(compilationOptionsBuilder).build()
)

fun Project.androidLibrary(
    compilationOptionsBuilder: CompilationOptionsBuilder.() -> Unit = { },
    libraryOptionsBuilder: AndroidLibraryOptionsBuilder.() -> Unit = { },
) = applyAndroidLibrary(
    androidLibraryOptions = AndroidLibraryOptionsBuilder().apply(libraryOptionsBuilder).build(),
    compilationOptions = CompilationOptionsBuilder().apply(compilationOptionsBuilder).build()
)

fun Project.jvmLibrary(builderAction: CompilationOptionsBuilder.() -> Unit = { }) =
    applyJvmLibrary(
        compilationOptions = CompilationOptionsBuilder().apply(builderAction).build()
    )

JVMモジュールの作成とプラットフォームのデコレーション適用

1 - core 内に threading という新しいモジュールを作成します。

2 - この新しいモジュールを settings.gradle.kts に含めます。

include(":app", ":features:details", ":features:home", ":core:designsystem", ":core:threading")

3 - プロジェクトを同期した後、build.gradle.kts ファイルを作成し、このモジュールのオプションを設定します。

import com.rsicarelli.kplatform.jvmLibrary

plugins {
    kotlin("jvm")
}

jvmLibrary()

dependencies {
    api(libs.kotlinx.coroutines.core)
    testApi(libs.kotlinx.coroutines.test)
}

成功!

IDEはこのライブラリが純粋なJVMであると知らせます。

IntelliJを使用していますが、Android Studioでも異なるアイコンが表示されます。

次の記事では、 Kotlinコンパイラの実験機能のいくつかを包含するためにコンパイルをどのようにカスタマイズするかを学びます。

こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/rsicarelli/android-plataforma-parte-13-incluindo-modulos-puro-jvm-4f61