如何在iOS和Android的應用程式中共享Kotlin程式碼的功能
在本文中,我將使用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和工具,更多細節請訪問 ofollow,noindex">蘋果開發者網站 。
注意:我將使用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操作提供實際的支援。有關多平臺專案的更詳細說明,請點 此處 瞭解。
下表,是我對以上內容的梳理。
現在是時候在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裝置或模擬器上執行。
現在我可以看到在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。這樣的庫有:
· ktor ;