如何从 Android Studio 加速 Kotlin Multiplatform 的 iOS 应用程序构建

Posted

技术标签:

【中文标题】如何从 Android Studio 加速 Kotlin Multiplatform 的 iOS 应用程序构建【英文标题】:How to speed up iOS app builds of Kotlin Multiplatform from Android Studio 【发布时间】:2021-08-09 13:47:30 【问题描述】:

我有一个包含 ios 应用、android 应用和共享 KMP 模块的 KMP 项目。 当我尝试从 Android Studio 构建 iOS 应用程序(以便能够对其进行调试)时,构建成功。 ☺️

问题是构建总是(当您更改代码时)需要大约 12 分钟 ????⏰。当您调试并经常测试给定代码是否有效时,这会很长。 90% 的构建时间用于运行这些任务:

> Task :kmpcorelib:linkDebugFrameworkIosArm64
> Task :kmpcorelib:linkDebugFrameworkIosX64
> Task :kmpcorelib:linkKMPCoreDebugFrameworkIosArm64
> Task :kmpcorelib:linkKMPCoreDebugFrameworkIosX64
> Task :kmpcorelib:linkKMPCoreReleaseFrameworkIosArm64
> Task :kmpcorelib:linkKMPCoreReleaseFrameworkIosX64
> Task :kmpcorelib:linkReleaseFrameworkIosArm64
> Task :kmpcorelib:linkReleaseFrameworkIosX64

我的问题是:有没有办法在不运行所有这些任务的情况下在 iOS 模拟器上运行应用程序?

不知道为什么工作室需要链接RELEASE版本的框架。

我也不明白为什么它在 X64 模拟器上运行时必须运行 iOSArm64 变体。

运行linkDebugFrameworkIosArm64linkKMPCoreDebugFrameworkIosArm64有什么区别不是重复吗?

PS:我通过单击 Android Studio 中的默认配置来运行 iOS buld,而不是一些自定义 gradle 脚本。

这是 KMP build.gradle 的样子:

plugins 
    id("com.android.library")
    kotlin("multiplatform")
    kotlin("native.cocoapods")
    kotlin("plugin.serialization") version "1.5.0"
    id("com.prof18.kmp.fatframework.cocoa") version "0.0.1"
    id("io.gitlab.arturbosch.detekt")
    id("maven-publish")

version = "1.3.11"
group = "com.betsys.kmpcorelib"
val podName = "KMPCore"
detekt 
    autoCorrect = true
    config = files("$rootDir/config/detekt.yml")
    baseline = file("$rootDir/config/baseline.xml")
    input = files("src/commonMain/kotlin")
    reports 
        html.enabled = true
        xml.enabled = true
        txt.enabled = true
    


// If you want to build iOS app from Android Studio - you have to put this config to comment - it somehow breaks the iOS build
fatFrameworkCocoaConfig 
    fatFrameworkName = podName
    outputPath = "$rootDir/../cocoapods"
    versionName = "1.16.1"

val ktorVersion = "1.5.4"
val napierVersion = "1.4.1"
val koinVersion = "3.0.1"
val kotlinxVersion = "1.4.1"
val kotlinxDatetime = "0.1.1"
kotlin 
    // We cant use simple ios() target now, since it doesnt support Arm32 architecture
    // For that reason we have specify each target explicitly
    // Same for folders with iOS code. We havet to use duplicit iosX64Main etc. instead of one iosMain folder
    ios("ios") 
        binaries.framework(podName)
    
    android 
        publishLibraryVariants("debug")
        publishLibraryVariantsGroupedByFlavor = true
    
    sourceSets 
        val commonMain by getting 
            dependencies 
                implementation("io.ktor:ktor-client-core:$ktorVersion")
                implementation("io.ktor:ktor-client-logging:$ktorVersion")
                implementation("io.ktor:ktor-client-mock:$ktorVersion")
                implementation("io.ktor:ktor-client-serialization:$ktorVersion")
                implementation("io.ktor:ktor-client-websockets:$ktorVersion")
                api("io.insert-koin:koin-core:$koinVersion")
                api("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxVersion-native-mt")
                api("org.jetbrains.kotlinx:kotlinx-datetime:$kotlinxDatetime")
                api("com.ionspin.kotlin:bignum:0.3.1-SNAPSHOT")
                implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.0")
            
        
        val commonTest by getting 
            dependencies 
                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))
                implementation( "io.mockk:mockk-common:1.11.0")
                implementation("com.ionspin.kotlin:bignum:0.3.1-SNAPSHOT")
            
        
        val androidMain by getting 
            dependencies 
                implementation("io.ktor:ktor-client-android:$ktorVersion")
                implementation("com.google.android.material:material:1.3.0")
                implementation("io.ktor:ktor-client-okhttp:$ktorVersion")
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinxVersion")
                implementation("com.jakewharton.timber:timber:4.7.1")
            
        
        val androidTest by getting 
            dependencies 
                implementation(kotlin("test-junit"))
                implementation("junit:junit:4.13.2")
                implementation("io.mockk:mockk:1.11.0")
            
        
        val iosMain by getting 
            dependencies 
                implementation("io.ktor:ktor-client-ios:$ktorVersion")
            
        
        val iosTest by getting
    

android 
    compileSdkVersion(30)
    sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
    defaultConfig 
        minSdkVersion(19)
        targetSdkVersion(30)
        versionCode = 1
        versionName = "1.0"
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    
    buildTypes 
        getByName("release") 
            isMinifyEnabled = false
        
    
    packagingOptions 
        excludes.add("META-INF/*.kotlin_module")
    
    compileOptions 
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    
    tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().all 
        kotlinOptions 
            jvmTarget = "1.8"
        
    

val packForXcode by tasks.creating(Sync::class) 
    val targetDir = File(buildDir, "xcode-frameworks")
    /// selecting the right configuration for the iOS
    /// framework depending on the environment
    /// variables set by Xcode build
    val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
    val sdkName: String? = System.getenv("SDK_NAME")
    val isiOSDevice = sdkName.orEmpty().startsWith("iphoneos")
    val framework = kotlin.targets
        .getByName<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>(
            if (isiOSDevice) 
                "iosArm64"
             else 
                "iosX64"
            
        )
        .binaries.getFramework(mode)
    inputs.property("mode", mode)
    dependsOn(framework.linkTask)
    from( framework.outputDirectory )
    into(targetDir)
    println("Build Folder => $targetDir")
    /// generate a helpful ./gradlew wrapper with embedded Java path
    doLast 
        val gradlew = File(targetDir, "gradlew")
        gradlew.writeText(
            "#!/bin/bash\n"
                    + "export 'JAVA_HOME=$System.getProperty("java.home")'\n"
                    + "cd '$rootProject.rootDir'\n"
                    + "./gradlew \$@\n"
        )
        gradlew.setExecutable(true)
    

tasks.build.dependsOn("packForXCode")

【问题讨论】:

【参考方案1】:

Android Studio 中的“默认配置”(例如assemblebuild gradle 任务)将构建所有“构建”任务,其中包括用于调试、发布以及用于 X64 和 Arm64 的 iOS 框架的链接架构。

注意到您在build.gradle.kts 文件中找到了packForXcode 任务吗?当您想为您的 iOS 应用程序(至少在本地)构建 KMM 模块时,这就是您想要调用的内容。使用该任务将只构建您需要的架构和配置(例如,在您的模拟器上使用X64DEBUG),您很快就会发现它要快得多。它应该在运行脚本阶段中调用,作为 Xcode 项目的构建步骤的一部分。

如果您在 Android Studio 中创建新项目时使用 Kotlin Multiplatform 应用程序模板,我相信这应该已自动添加到您的 Xcode 项目中。

您可以在此处阅读更多信息:Understand the KMM project structure | Kotlin Multiplatform Mobile Docs

【讨论】:

以上是关于如何从 Android Studio 加速 Kotlin Multiplatform 的 iOS 应用程序构建的主要内容,如果未能解决你的问题,请参考以下文章

如何在android studio 上用加速度传感器来得到x y z轴上的坐标?

markdown 加速Android Studio的终极指南

加速Android Studio/Gradle构建

加速Android Studio编译速度

Android Studio 1.3RC版 build加速

精粹加速Android Studio/Gradle构建