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

x
用微信扫描二维码
分享至好友和朋友圈

  

  在本文中,我将使用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.UIDeviceactual 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操作提供实际的支持。有关多平台项目的更详细说明,请点此处了解。

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

  

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

  
使用Android共享代码

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

  要包括从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设备或模拟器上运行。

  

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

  

  
创建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

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

  

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

  

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

  

  最后一步是让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构建至少执行一次。

  

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

  

  现在,我已经准备好开始编写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按钮,看到我的应用程序的运行。

  

  
总结

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

  · 在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;

  

  

特别声明:本文为网易自媒体平台“网易号”作者上传并发布,仅代表该作者观点。网易仅提供信息发布平台。

跟贴 跟贴 0 参与 0
© 1997-2019 网易公司版权所有 About NetEase | 公司简介 | 联系方法 | 招聘信息 | 客户服务 | 隐私政策 | 广告服务 | 网站地图 | 意见反馈 | 不良信息举报

嘶吼RoarTalk

不一样的互联网安全新视界

头像

嘶吼RoarTalk

不一样的互联网安全新视界

3248

篇文章

6996

人关注

列表加载中...
请登录后再关注
x

用户登录

网易通行证/邮箱用户可以直接登录:
忘记密码