如何在iOS和Android的应用程序中共享Kotlin代码的功能

xiaohui 技术 2018年10月23日发布
Favorite收藏

导语:在本文中,我将使用Kotlin的代码共享特性创建一个iOS和Android应用程序。对于Android,我将使用Kotlin/JVM,而对于iOS,我将使用Kotlin/Native。

在本文中,我将使用Kotlin的代码共享特性创建一个iOS和Android应用程序。对于Android,我将使用Kotlin/JVM,而对于iOS,我将使用Kotlin/Native。

你将在本文中学习到以下内容:

1.用Android Studio创建一个Android应用程序;

2.创建一个共享的Kotlin库:

2.1使用Android应用程序;

2.2启动Android应用程序;

3.用Xcode创建一个iOS应用程序:

3.1使用iOS应用程序中共享的Kotlin库;

3.2使用Swift的Kotlin;

3.3启动iOS应用程序

本文的目标是向你介绍如何在Kotlin中共享代码,及其共享代码提供的好处。虽然你以下看到的是一个简化的应用程序,但这只是为了方便讲解,不过以下所举的样本的内容均可以应用于实际应用程序,与其大小或复杂程度无关。

我将要创建的应用程序会简单的在Android上显示Kotlin Rocks on Android,在iOS上显示Kotlin Rocks on iOS <version>,我的想法是共享生成此消息的代码。

公用代码是“Kotlin Rocks on ${platformName()}”,其中platformName()是一个使用expect关键字声明的函数,在实际的适用过程中将特定于某个平台。

构建运行环境

本文的Android部分将使用Android Studio,也可以使用IntelliJ IDEA(java编程语言开发的集成环境)或Ultimate edition(一个测试软硬件系统信息的工具,它可以详细的显示出PC每一个方面的信息)。

应在IDE中安装Kotlin插件1.3.x及其以上版本,这可以通过IDE的设置(或首选项)中的Language & Frameworks | Kotlin更新部分来验证。

为iOS和macOS设备编译需要配有macOS主机操作系统,为此我需要安装和配置Xcode和工具,更多细节请访问苹果开发者网站

注意:我将使用IntelliJ IDEA 2018.3 EAP, Android Studio 3.2, Kotlin 1.3.0, Xcode 10.0, macOS 10.14, Gradle 4.10.2。

创建一个Android项目

我将通过Start New Android Project项目创建一个新的Android项目,如果使用IntelliJ IDEA,我需要在New Project向导的左侧面板中选择Android。

确保勾选包含Kotlin支持复选框非常重要,只有这样,我可以在向导的下一步中保留默认设置。然后我继续选择Empty Activity选项并点击Next,最后按Finish。

注意,如果使用Kotlin插件的预发布版本或EAP版本,IDE可能无法打开生成的项目,从而导致Gradle导入错误。这是因为build.gradle文件中没有引用正确的Maven存储库,可以通过将以下内容两次添加到每个repositories { .. } 块中来解析它:

maven {url 'https://dl.bintray.com/kotlin/kotlin-eap'}

Kotlin/Native插件需要更新版本的Gradle(一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具),这可以让我修补gradle / wrapper / gradle-wrapper.properties,并使用以下distrubutionUrl:

distributionUrl = https \:/ /services.gradle.org/distributions/gradle-4.10.2-all.zip

我需要刷新Gradle项目设置来应用这些更改,点击Sync Now链接或使用Gradle工具窗口,从root Gradle项目的上下文菜单中点击refresh操作。

此时,我应该就能够编译并运行Android应用程序。

创建共享模块

本文的目标是展示如何让Kotlin代码在Android和iOS之间进行共享。让我从使用平台间共享的代码创建SharedCode项目开始,我将在该项目中创建几个新文件。

添加Kotlin来源

我的想法是根据平台的不同,让每个平台都显示以下类似的文本信息: Kotlin Rocks on Android以及Kotlin Rocks on iOS。这样,我就可以重用生成消息的方式。以下就是我在SharedCode/src/commonMain/kotlin/common.kt下创建的主文件:

package org.kotlin.mpp.mobile

expect fun platformName(): String

fun createApplicationScreenMessage() : String {
  return "Kotlin Rocks on ${platformName()}"
}

这是很通常的代码部分,这些生成最终消息的代码,期望平台从expect fun platformName(): String函数提供平台名称。而我将使用的是来自Android和iOS应用程序的createApplicationScreenMessage。

现在,我需要在SharedCode/src/androidMain/kotlin/ actu.kt中为Android创建应用文件:

package org.kotlin.mpp.mobile

actual fun platformName(): String {
  return "Android"
}

我在SharedCode/src/iosMain/kotlin/ actu.kt中为iOS目标创建了一个类似的文件:

package org.kotlin.mpp.mobile

import platform.UIKit.UIDevice

actual fun platformName(): String {
  return UIDevice.currentDevice.systemName() +
         " " +
         UIDevice.currentDevice.systemVersion
}

在这里,我可以使用来自Apple UIKit框架的UIDevice类,它在Java中不可用,它只能在Swift和Objective-C中可用。Kotlin/Native编译器附带了一组预先导入的框架,所以我可以使用UIKit框架,而不需要额外的步骤。Objective-C和Swift互操作在这里有详细介绍。

更新Gradle脚本

SharedCode项目应该生成几个工具包括:

1.Android项目的JAR文件,来自androidMain源代码集;

2.苹果公司的框架:

2.1 iOS设备和App Store (arm64 目标);

2.2 iOS模拟器(x86_64目标)

看看我是如何更新Gradle脚本的?

首先,我将新项目添加到settings.gradle文件中,只需将以下代码行添加到文件末尾即可:

include ':SharedCode'

接下来,我需要使用以下内容创建SharedCode/build.gradle文件:

apply plugin: 'kotlin-multiplatform'

kotlin {
    targets {
        final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos") \
                              ? presets.iosArm64 : presets.iosX64

        fromPreset(iOSTarget, 'iOS') {
            compilations.main.outputKinds('FRAMEWORK')
        }

        fromPreset(presets.jvm, 'android')
    }

    sourceSets {
        commonMain.dependencies {
            api 'org.jetbrains.kotlin:kotlin-stdlib-common'
        }

        androidMain.dependencies {
            api 'org.jetbrains.kotlin:kotlin-stdlib'
        }
    }
}

// workaround for https://youtrack.jetbrains.com/issue/KT-27170
configurations {
    compileClasspath
}

构建跨平台Gradle项目

SharedCode/build.gradle文件使用kotlin-multiplatform插件来实现我需要的东西,在SharedCode/build.gradle文件中,我定义了几个常见的目标,android和iOS。每个目标都有自己的平台。common目标包含每个平台编译中的Kotlin通用代码,允许expect声明。其他目标为来自common目标的所有expect操作提供实际的支持。有关多平台项目的更详细说明,请点此处了解。

下表,是我对以上内容的梳理。

微信截图_20181020160053.png

现在是时候在Android Studio中再次刷新Gradle项目了。点击黄色条纹上的Sync,或者使用Gradle工具窗口,在root Gradle项目的上下文菜单中点击Refresh操作。SharedCode项目现在应该可以被IDE识别了。现在,我已经准备好使用Android和iOS应用程序的SharedCode库了。

使用Android共享代码

在本文中,由于我希望尽量减少Android项目的更改,因此我在SharedCode项目中添加了一个普通的依赖项。不过你也可以在Android Gradle项目中直接使用kotlin-multiplatform插件,而不是kotlin-android插件。有关更多信息,请点此了解。

要包括从SharedCode项目到Android项目的依赖关系,就需要修补app/build.gradle文件并将以下代码行包含在dependencies { .. }块中:

implementation project(':SharedCode')

我需要将id分配给活动的TextView控件以便从代码中访问它,我会修补app/src/main/res/layout/activity_main.xml文件(如果我在新项目向导中更改了它,名称可能会有所不同),并向<TextView>元素添加更多属性:

   android:id="@+id/main_text"
        android:textSize="42sp"
        android:layout_margin="5sp"
        android:textAlignment="center"

接下来,让我将以下代码行包含在/app/src/main/java/<package>/MainActivity.kt文件的MainActivity类中,直到onCreate方法的末尾:

findViewById<TextView>(R.id.main_text).text = createApplicationScreenMessage()

使用IDE的项目时包括以下缺失的导入行:

import org.kotlin.mpp.mobile.createApplicationScreenMessage

将它放到/app/src/main/java/<package>/MainActivity.kt文件中。

现在我就有了TextView,它将显示由共享代码函数createApplicationScreenMessage()创建的文本。它在Android上显示了Kotlin Rocks on Android。让我看看它是如何工作的。

运行Android应用程序

点击App运行配置,让我的项目在真正的Android设备或模拟器上运行。

1.png

现在我可以看到在Android模拟器中运行的应用程序:

2.png

创建iOS应用程序

我打开Xcode并选择Create a new Xcode project选项。在该对话框中,我会选择iOS目标并选择Single View App。使用默认值填写下一页,并使用KotlinIOS或其他内容作为产品名称。我会选择Swift作为语言(也可以使用Objective-C),此时,我会指示Xcode将以上设置好的项目放入我项目下的运行文件夹中,稍后我将在配置文件中使用相对路径。

创建的iOS应用程序可以在iOS模拟器或iOS设备上运行,设备运行可能需要Apple开发人员帐户并颁发开发人员证书,而Xcode尽最大努力指导我完成整个过程,以确保我可以在iPhone模拟器或设备上运行该应用程序。

在Xcode中设置框架依赖性

SharedCode会构建生成用于Xcode项目的iOS框架。所有框架都在SharedCode/build/bin文件夹中。它为每个框架目标创建调试和发布版本。这些框架的路径如下:

SharedCode/build/bin/iOS/main/debug/framework/SharedCode.framework
SharedCode/build/bin/iOS/main/release/framework/SharedCode.framework

我使用Gradle脚本中的条件来为框架选择目标平台,它可以是iOS arm64,也可以是iOS x86_64,这取决于环境变量。

优化Gradle构建脚本

我需要根据Xcode项目中的选定目标提供正确的框架,这取决于在Xcode中选择的目标配置。另外,我想让Xcode在构建之前为我编译框架,我需要在SharedCode / build.gradle Gradle文件的末尾包含附加任务。

task packForXCode(type: Sync) {
    final File frameworkDir = new File(buildDir, "xcode-frameworks")
    final String mode = System.getenv('CONFIGURATION')?.toUpperCase() ?: 'DEBUG'

    inputs.property "mode", mode
    dependsOn kotlin.targets.iOS.compilations.main.linkTaskName("FRAMEWORK", mode)

    from { kotlin.targets.iOS.compilations.main.getBinary("FRAMEWORK", mode).parentFile }
    into frameworkDir

    doLast {
        new File(frameworkDir, 'gradlew').with {
            text = "#!/bin/bash\nexport 'JAVA_HOME=${System.getProperty("java.home")}'\ncd '${rootProject.rootDir}'\n./gradlew \$@\n"
            setExecutable(true)
        }
    }
}

tasks.build.dependsOn packForXCode

请注意,如果使用早于4.10版本的Gradle,则任务可能无法正常工作。在本文中,我已将其升级到4.10.2。

现在,我会切换回Android Studio并从Gradle工具窗口执行SharedCode项目的构建目标。该任务查找由Xcode构建设置的环境变量,并将框架的正确变体复制到SharedCode/build/xcode-frameworks文件夹中。然后,我将该文件夹中的框架包含到构建中。

设置Xcode

我会将SharedCode框架添加到Xcode项目中。为此,我要点击project navigator的根节点并选择目标设置。接下来,我点击嵌入式二进制文件部分中的+,点击对话框中的Add Other…按钮,从磁盘中选择框架。我可以选择以下文件夹:

SharedCode/build/xcode-frameworks/SharedCode.framework

然后我会看到类似的东西:

xcode-general.png

另外,我还需要禁用项目中的Bitcode功能。Kotlin/Native生成的是完全适用于本机的二进制文件,而不是LLVM的bitcode,因此我需要导航到Build Settings选项卡,选择下面的All子选项卡,然后在搜索字段中输入bitcode,选择No作为“启用Bitcode”选项。

12.png

现在我需要向Xcode解释,在哪里寻找框架。我需要添加相对路径$(SRCROOT)/../../SharedCode/build/xcode框架到 Search Paths | Framework Search Paths选项。再次打开Build Settings选项卡,选择下面的All子选项卡,然后在Search字段中输入Framework Search Paths,以便轻松找到该选项,然后,Xcode将在用户界面中显示替换路径。

13.png

最后一步是让Xcode调用我的Gradle构建,以便在每次运行之前准备好共享代码框架。此时,我需要打开Build Phases选项卡并单击+以添加New Run Script Phase并将以下代码添加到其中。

cd "$SRCROOT/../../SharedCode/build/xcode-frameworks"
./gradlew :SharedCode:packForXCode

注意,这里我使用的是$SRCROOT/../..作为我Gradle项目的根路径。它可以取决于创建Xcode项目的方式,另外,我使用生成的SharedCode / build / xcode-frameworks / gradlew脚本,packForXCode任务生成它。我假设在新设备上打开Xcode项目之前,Gradle构建至少执行一次。

14.png

我应该将创建的构建阶段拖到列表的顶部:

15.png

现在,我已经准备好开始编写iOS应用程序,并使用刚刚使用过的Kotlin代码。

从Swift调用Kotlin代码

请记住,我的目标是在屏幕上显示文本消息。如你以上所见,我的iOS应用程序在屏幕上什么内容都不会显示。要让它用文本消息显示UILabel(UILabel继承自UIView是iOS中使用非常频繁的一个视图控件一般用于显示文字)。我需要使用以下代码替换ViewController.swift文件的内容:

import UIKit
import SharedCode

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 21))
        label.center = CGPoint(x: 160, y: 285)
        label.textAlignment = .center
        label.font = label.font.withSize(25)
        label.text = CommonKt.createApplicationScreenMessage()
        view.addSubview(label)
    }
}

我会使用import SharedCode函数来将共享代码导入我的框架,并会使用Kotlin代码编译Kotlin/Native。接下来,我将通过它调用称为CommonKt.createApplicationScreenMessage()的Kotlin函数。更多细节,请点查看。

现在,我已准备好在模拟器或iOS设备上启动应用程序。

运行iOS应用程序

现在,我可以点击Xcode中的Run按钮,看到我的应用程序的运行。

iPhone-emulator-kotlin-rocks.png

总结

在本文中我讲到了以下知识点:

· 在Android Studio中创建了一个Android应用程序;

· 在Xcode中创建了一个iOS应用程序;

· 新增了Kotlin跨平台子项目;

· 使用共享的Kotlin代码;

· 将其编译为Android Jar;

· 将其编译为iOS 框架;

· 让Android和iOS重新使用共享的Kotlin代码;

你可以在GitHub上找到本文的全部代码。

本文只是iOS和Android以及其他平台与Kotlin,Kotlin/Native和Kotlin多平台项目之间共享Kotlin代码的一个研究样本。你可以在实际中,将此研究应用到更复杂的情况中。

在平台之间共享代码是一项高难度的技术,但如果没有我在Android,JVM或iOS平台中使用的丰富API,可能很难实现。不过,也可以使用多平台库来解决这个问题。它们直接在常见的Kotlin代码中引入了丰富的API。这样的库有:

· kotlinx.coroutines

· kotlinx.io

· kotlinx.serialization

· ktor

· ktor-http-client

本文翻译自:http://kotlinlang.org/docs/tutorials/native/mpp-ios-android.html如若转载,请注明原文地址: http://www.4hou.com/technology/14127.html
点赞 1
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论