JetBrains開發者日見聞(一)之Kotlin/Native 嚐鮮篇

簡述: 今天咱們來說點Kotlin中比較時髦的東西,有的人可能會說:「不像你以前的風格啊,以前的文章不是一直在死扣語法以及語法糖背後祕密。當你還在死扣泛型語法的時候,別人的文章早就說了Kotlin/Native和Kotlin1.3的新特性」。瞬間感受本身out了,今天咱們就說說這些時髦的東西,也許你能看到一些和別人不同的東西哦。前端

前段時間大家的熊貓小哥哥(也就是我),因爲對Kotlin過分熱愛,一天偶然看到2018 JetBrains開發者日-Kotlin專場活動,腦殼一熱,瞬間心動了,立刻就買了門票和火車票去北京(第一次一我的去北京)參加活動了。由於看到有Kotlin中文社區兩位大佬(這兩位大佬是我一年多之前開始寫Kotlin的時候就關注了他們)的演講日程以及JetBrains資深佈道師Hali的演講,沒有過多思考直接買票,不要慫就是幹。最後順便和Kotlin社區的大佬們面個基啥的,謝謝大佬們的熱情款待。這次北京之行收穫挺多的,有時候知道一些最新技術方向和動態會比你埋頭閉門造車好的不少。ios

由於在個人公衆號上(Kotlin開發者聯盟),有一些小夥伴但願我能從北京的開發者會上帶點東西回來,因此總結了一下結合本身實際的開發,給你們帶來如下幾篇文章。git

  • 一、Kotlin/Native1.0 Beta(嚐鮮篇)
  • 二、Kotlin中1.3版本新特性都有哪些?
  • 三、Kotlin中的Coroutine(協程)在Android上應用(協程學前班篇)
  • 四、Ktor異步框架初體驗(Ktor學前班篇)

那麼,今天就開始第一篇,看過一些大佬寫關於Kotlin/ Native的文章,基本上都是翻譯了Kotlin Blog的官網博客, 具體如何實踐的仍是比較少的。今天我不打算這麼講,既然今天的主題是時髦那就講點有意思的東西。一塊兒來看下今天提綱:程序員

1、從新認識Kotlin語言

在開始以前,我以爲有必要一塊兒從新來認識一下Kotlin這門語言,不少人一直都認爲它不就是門JVM語言和Java、Scala同樣都是跑在JVM虛擬機上。其實Kotlin並不只僅是一門JVM語言,它的野心是真的大,JVM語言已經沒法知足它的雄心壯志了。它是一門多平臺的靜態編譯型語言,它能夠用於JVM上(只不過在JVM層面比較出名而已,致使不少人都認爲它是門JVM語言),實則它能夠編譯成JavaScipt運行在瀏覽器中也能夠編譯成IOS的可運行文件跑在LLVM上github

2、Kotlin/Native的基本介紹

用官方的話來講Kotlin / Native是一種將Kotlin代碼編譯爲本機二進制文件的技術,能夠在沒有虛擬機的狀況下運行。它是基於LLVM的後端,用於Kotlin編譯器和Kotlin標準庫的本機實現shell

Kotlin/Native目前支持如下平臺:編程

  • --- iOS (arm32, arm64, emulator x86_64)
  • --- MacOS (x86_64)
  • --- Android (arm32, arm64)
  • --- Windows (mingw x86_64)
  • --- Linux (x86_64, arm32, MIPS, MIPS little endian)
  • --- WebAssembly (wasm32)

爲了更好說明Kotlin/Native能力,下面給出張官方的Kotlin/Native能力圖:swift

對於Kotlin/Native以前一直沒有去玩過,只是常常聽到社區小夥伴們說編譯起來巨慢,感受好時髦啊。抱着好奇心,而且也符合咱們這篇文章時髦的主題,決定一步步帶你們玩一玩。後端

3、Kotlin/Native開發IOS HelloWorld

一、須要準備的開發工具

  • AppCode 2018.1(建議下載最新版本,這裏不是最新版本不過也能玩哈,最新版本應該到了2018.3)
  • Kotlin/Native Plugin 181.5087.34(注意: 插件和AppCode IDE的版本匹配問題,建議把IDE安裝好,而後IDE搜索下載會默認給最佳匹配的插件版本的)
  • Xcode 9.2(注意: 這裏Xcode版本須要AppCode版本匹配,不然會有問題的,不過不匹配的話IDE會有提示的,建議若是AppCode 2018.1(Xcode 9.2), AppCode 2018.3(Xcode 10.0))

二、建立一個Kotlin/Native項目

第一步: 選擇左側的Kotlin/Native, 並選擇右側的Sing View App with a Kotlin/Native Framework瀏覽器

第二步: 填寫項目名和包名,選擇語言Swift(這裏先以Swift爲例)

第三步: 最後finish便可建立完畢Kotlin/Native項目,建立完畢後項目結構以下

四、運行Kotlin/Native項目

若是你比較幸運跑起來的話,效果應該是在模擬器裝一個APP而且起了一個空白頁,終端上輸出了"Hello from Kotlin!"的Log,相似這樣:

注意: 可是你是真題測試,並且Run頂部默認只有一個IOS Device選項的話,而後你又點了Run 說明並且會報以下錯誤

這個問題是由於默認IOS Device選項是表示用真機調試哈,而後這邊就須要一個IOS開發者帳號。設置開發者帳號的話,建議使用Xcode去打開該項目而後給該項目配置一個開發者帳號。

設置完畢Xcode後,AppCode會自動檢測到刷新的。

4、Kotlin/Native開發IOS 運行原理分析

看到上面IOS HelloWorld項目運行起來,你們有沒有思考一個問題,Kotlin的代碼的代碼是怎麼在IOS設備上跑起來呢?

實際上,在這背後使用了一些腳本和工具在默默支撐着整個項目的運行,如前所述,Kotlin / Native平臺有本身的編譯器,但每次想要構建項目時手動運行它明顯不是高效的。 因此Kotlin團隊了選擇Gradle。Kotlin / Native使用Gradle構建工具在Xcode中自動完成Kotlin / Native的整個構建過程。在這裏使用Gradle意味着開發人員能夠利用其內部增量構建架構,只需構建和下載所需內容,從而節省開發人員的寶貴時間。

若是,你還對上述有點疑問不妨一塊兒來研究下Kotlin/Native項目中的構建參數腳本:

  • 打開構建腳本是須要在Xcode中打開的,具體能夠參考以下圖:

經過以上項目能夠分析到在Xcode中編譯一個Kotlin/Native項目,實際上在執行一段shell腳本,並在shell腳本執行中gradlew命令來對Kotlin/Native編譯,該腳本調用gradlew工具,該工具是Gradle Build System的一部分,並傳遞構建環境和調試選項。 而後調用一個konan gradle插件實現項目編譯並輸出xxx.kexe文件,最後並把它複製到iOS項目構建目錄("$TARGET_BUILD_DIR/$EXECUTABLE_PATH")。

最後來看下Supporting Files中的build.gradle構建文件,裏面就引入了konan插件(Kotlin/Native編譯插件), 有空的話建議能夠深刻研究下konan插件,這裏其實也是比較淺顯分析了下整個編譯過程,若是深刻研究konan插件源碼的話,更能透過現象看到Kotlin/Native本質,這點纔是最重要的。

buildscript {
    ext.kotlin_version = '1.2.0'
    repositories {
        mavenCentral()
        maven {
            url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies"
        }
    }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:0.7"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

apply plugin: 'kotlin'

repositories {
    mavenCentral()
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib"
}

apply plugin: 'konan'

konan.targets = [
    'ios_arm64', 'ios_x64'
]
        
konanArtifacts {
    program('KotlinNativeOC')
}
        

複製代碼

5、Kotlin/Native項目結構分析

一、Kotlin/Native + Swift項目結構分析

咱們知道main函數是不少應用程序的入口,ios也不例外,在AppDelegate.swift中有@UIApplicationMain的註解,這裏就是APP啓動的入口。

@UIApplicationMain //main函數註解入口,因此AppDelegate類至關於啓動入口類
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?//默認加了UIWindow



    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // KNFKotlinNativeFramework class is located in the framework that is generated during build.
    // If it is not resolved, try building for the device (not simulator) and reopening the project
    NSLog("%@", KNFKotlinNativeFramework().helloFromKotlin())//注意: 這裏就是調用了Kotlin中的一個helloFromKotlin方法,並把返回值用Log打印出來,因此你會看到App啓動的時候是有一段Log被打印出來
                  
    return true
    }
    ...
}
複製代碼

KotlinNativeFramework類

class KotlinNativeFramework {
    fun helloFromKotlin() = "Hello from Kotlin!" //返回一個Hello from Kotlin!字符串
}
複製代碼

可是呢,有追求的程序員絕對不能容許跑出來的是一個空白頁面,空白頁面那還怎麼裝逼呢? 哈哈。在ViewController.swift中的viewDidLoad函數中加入一個文本(UILabel)。

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(15)
        label.text = "Hello IOS, I'm from Kotlin/Native"
        view.addSubview(label)
    }
    override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
    }
}
複製代碼

最後從新run一遍,效果以下:

二、Kotlin/Native + Objective C項目結構分析

在IOS同事幫助下,進一步瞭解IOS APP啓動基本知識,這將有助於咱們接下來改造咱們項目結構,使得它更加簡單,徹底能夠刪除額外的Swift代碼,包括APP啓動代理那塊都交由Kotlin來完成。

  • 第一步: 先建立一個Kotlin/Native + OC 的項目,這裏就不重複建立過程,直接把OC目錄結構給出:

  • 第二步: 能夠看到OC與Swift項目結構差很少哈,能夠看到其中有幾個重要的文件,main.m、AppDelegate.m、ViewController.m main.m APP啓動入口,至關於main函數,先從main函數入手,而後一步步弄清整個啓動流程。
#import <UIKit/UIKit.h>
#import "AppDelegate.h"


int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));//這裏也調用了AppDelegate類
    }

}
複製代碼
  • 第三步: 而後轉到AppDelegate.m,能夠看到在didFinishLaunchingWithOptions函數中調用了KNFKotlinNativeFramework中的helloFromKotlin函數。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // KNFKotlinNativeFramework class is located in the framework that is generated during build.
    // If it is not resolved, try building for the device (not simulator) and reopening the project
    NSLog(@"%@", [[[KNFKotlinNativeFramework alloc] init] helloFromKotlin]);//注意這裏調用helloFromKotlin,並輸出日誌
                  
    return YES;
}
複製代碼

三、Kotlin/Native + Kotlin項目結構分析

到這裏不少人就會問了,看你上面說了那麼並無看到你Kotlin在作什麼事,全是Swift和OC在作APP啓動。如今就是告訴你Kotlin如何去替代它們作APP啓動的事了。

  • 先新建立一個項目,此次建立的再也不是Sing View App with a Kotlin/Native Framework, 而是一個Application項目。

  • 生成後的目錄文件全是Kotlin文件,具體以下:

  • 生成的main.kt替代main.m,並設置對應啓動的AppDelegate
import kotlinx.cinterop.autoreleasepool
import kotlinx.cinterop.cstr
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toCValues
import platform.Foundation.NSStringFromClass
import platform.UIKit.UIApplicationMain

fun main(args: Array<String>) {
    memScoped {
        val argc = args.size + 1
        val argv = (arrayOf("konan") + args).map { it.cstr.getPointer(memScope) }.toCValues()

        autoreleasepool {
            UIApplicationMain(argc, argv, null, NSStringFromClass(AppDelegate))//注意: 在這裏設置對應啓動的AppDelegate
        }
    }
}
複製代碼
  • 生成AppDelegate替代原來的AppDelegate.m,而且在內部設置好啓動的Window.
import kotlinx.cinterop.initBy
import platform.Foundation.NSLog
import platform.UIKit.*

class AppDelegate : UIResponder(), UIApplicationDelegateProtocol {
    override fun init() = initBy(AppDelegate())
    private var _window: UIWindow? = null
    override fun window() = _window
    override fun setWindow(window: UIWindow?) { _window = window }
    override fun application(application: UIApplication, didFinishLaunchingWithOptions: Map<Any?, *>?): Boolean {//監聽APP啓動完成,打印Log
        NSLog("this is launch from kotlin appDelegate")
        return true
}
    companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta//注意:必定得有個companion object不然在main函數NSStringFromClass(AppDelegate)會報錯
}
複製代碼
  • 再生成一個ViewController,這個ViewController很相似Android中的Activity。
import kotlinx.cinterop.*
import platform.Foundation.*
import platform.UIKit.*

@ExportObjCClass
class ViewController : UIViewController {

    constructor(aDecoder: NSCoder) : super(aDecoder)
    override fun initWithCoder(aDecoder: NSCoder) = initBy(ViewController(aDecoder))

    @ObjCOutlet
    lateinit var label: UILabel

    @ObjCOutlet
    lateinit var textField: UITextField

    @ObjCOutlet
    lateinit var button: UIButton

    @ObjCAction
    fun buttonPressed() {
        label.text = "Konan says: 'Hello, ${textField.text}!'"
    }
}
複製代碼

運行出來的效果以下:

6、Kotlin/Native開發一個地圖Demo

一、IOS項目ViewController與組件綁定過程分析

看到上面的運行Demo,你們有沒有在思考一個問題IOS項目中的ViewController是怎麼和UI組件綁定在一塊兒的呢?我我的認爲這個很重要,換句話說這就是IOS開發最基本的套路,若是這個都不弄明白的話,下面Demo開發就是雲裏霧裏了,掌握了這個基本套路的話,做爲一個Android開發者,你基本上就能夠在IOS項目開發中任意折騰了。

  • 第一步: 在kotlin目錄下新建一個KNMapViewController類,而且它去繼承UIViewController以及實現MKMapViewDelegateProtocol接口,並重寫viewDidLoad()函數。而且在viewDidLoad函數實現map地圖基本配置。
//導入Kotlin以與Objective-C和一些Cocoa Touch框架互操做。
import kotlinx.cinterop.*
import platform.CoreLocation.CLLocationCoordinate2DMake
import platform.Foundation.*
import platform.MapKit.MKCoordinateRegionMake
import platform.MapKit.MKCoordinateSpanMake
import platform.MapKit.MKMapView
import platform.MapKit.MKMapViewDelegateProtocol
import platform.UIKit.*

@ExportObjCClass//注意: @ExportObjCClass註解有助於Kotlin建立一個在運行時可查找的類。
class KNMapViewController: UIViewController, MKMapViewDelegateProtocol {
    @ObjCOutlet //注意: @ObjCOutlet註解很重要,主要是將mMapView屬性設置爲outlet。這容許您將Main.storyboard中的MKMapview連接到此屬性。
    lateinit var mMapView: MKMapView
    constructor(aDecoder: NSCoder) : super(aDecoder)
    override fun initWithCoder(aDecoder: NSCoder) = initBy(KNMapViewController(aDecoder))
    override fun viewDidLoad() {
        super.viewDidLoad()
        val center = CLLocationCoordinate2DMake(32.07, 118.78)
        val span = MKCoordinateSpanMake(0.7, 0.7)
        val region = MKCoordinateRegionMake(center, span)

        with(mMapView) {
            delegate = this@KNMapViewController
            setRegion(region, true)
        }
    }
}
複製代碼
  • 第二步: 用Xcode打開項目中的Main.storyboard,刪除原來自動生成一些視圖組件(若是你處於AppCode中開發項目,實際上直接在AppCode中雙擊Main.storyboard就會自動使用Xcode打開當前整個項目,並打開這個項目)

  • 第三步: 給當前空的視圖綁定對應ViewController,這裏是KNMapViewController

  • 第四步: 在當前空的視圖中添加一個map view組件而且設置組件的約束條件。

  • 第五步: 右擊組件MKMapView能夠看到黑色對話框,裏面Referencing Outlets還空的,說明當前ViewController沒有和MKMapView組件綁定

  • 第六步: 配置outlet,這裏說下AppCode很坑爹地方,須要手動去source code中手動配置outlet,選中main.storyboard右擊open as 而後選擇打開source code

  • 第七步: 在view和viewController結尾標籤之間配置connection
    配置的code以下:
<connections>
   <outlet property="mMapView" destination="dest id" id="generate id"/>
</connections>
<!--property屬性值就是KNMapViewController中的mMapView變量名;destination屬性值是一個map view標籤中id(能夠在subviews標籤內的mapView標籤中找到id), id屬性則是自動生成的,能夠按照格式本身之指定一個,只要不出現重複的id便可-->
複製代碼

配置結果以下:

  • 第八步: 檢驗是否綁定成功, 回到main.stroyboard視圖,右擊組件查看黑色框是否出現以下綁定關係,出現了則說明配置成功。

二、接着上述配置步驟,就能夠回到AppCode中運行項目了

以上的運行結果就說明了,咱們Demo已經運行成功了。而且我已經把這次Kotlin/Native項目都放到了GitHub上,若是感興趣的小夥伴能夠clone下來玩一玩這個時髦的鬼東西。若是以爲對你有幫助,還請大佬們給個star。

Kotlin/Native Demo GitHub

7、Kotlin/Native開發體驗分析

Kotlin/Native目前仍是處於1.0的beta,因此仍是有不少的地方是不讓人滿意的。下面我總結此次Kotlin/Native開發體驗的優缺點:

優勢:

經過上述的幾個例子,能夠明顯代表Kotlin/Native語言層面跨平臺能力仍是很強的,和OC,Swfit項目操做性也很強,該有的基本上都已經實現了,因此對於它後續發展仍是很是值得關注的。據我瞭解到,Kotlin團隊目前首要重心就是在Kotlin/Native這一塊,但願他們能給咱們帶來更多關於Kotlin/Native的驚喜。

缺點

缺點仍是有不少的:

  • 一、Kotlin/Native編譯速度有點慢的驚人,不過運行速度仍是不錯的。
  • 二、AppCode中用Kotlin開發IOS項目,沒有很強的代碼提示,不少API導包都比較麻煩,對於初學者更是一臉懵逼。
  • 三、AppCode中的Kotlin/Native插件竟然沒提供右鍵建立Kotlin類、接口之類,包括官方也給出這是個問題不過提供一個fix方案。

最後能夠測試一下:

  • 四、AppCode中使用Kotlin開發IOS項目,配置outlet還得切換到在Xcode手工操做,但願後續改進。
  • 五、AppCode耗內存還大了,比AndroidStudio還厲害,多開了兩個項目,基本就是每操做IDE就在轉圈圈,但願後續改進。

8、聊點關於Kotlin/Native和Flutter框架那點事

一、簡述:

其實關於這個主題,我是不太想去說的,由於我不想引發編程語言界的口戰。可是總有人喜歡去把Kotlin/Native和Flutter放在一塊兒去作對比,由於他們好像都有一個共同點就是跨平臺都能開發Android、IOS應用。而且在這次Jetbrains開發者日上就有參會嘉賓在會上問官方佈道師Hali,Kotlin/Native和Flutter有什麼不同,優點在哪?

二、提出我的觀點:

針對這個問題我說下我的的觀點:

首先,我我的是很是看好Flutter這個移動端跨平臺框架,它可以徹底抹平Android,IOS API層面的差別性。換句話說就是一個小白,也許他不懂Java,OC或Swift,他只要熟悉dart而且熟悉Flutter框架API就能開發出Android和IOS兩個原生般體驗的應用。確實很爽啊,不管站在公司節約人力和維護成本仍是開發者技術成本角度考慮都是不錯的選擇。但是從另外一角度能夠想象下一旦造成這樣的局面,不少新項目都是用dart開發,swift和oc語言對於新的開發者而言你會去用嗎? 蘋果爸爸貌似就會不開心了,這其中故事你們能夠本身去想像了(固然也許將來不是我說的那樣發展,純屬我的猜測的哈)。

而後,我說下Kotlin/Native吧,其實Kotlin/Native和Flutter不是一個層面東西,不太很好作對比,Kotlin/Native更多的是語言編譯器層面,而Flutter是框架層面。二者根本就不是一個世界的。Flutter是本身實現一套渲染機制和UI引擎,而且有着豐富Development API。而Kotlin/Native更多關注是編譯器如何將kt源碼編譯成IOS LLVM可運行的二進制碼,可是Kotlin/Native並無像Flutter同樣在API層面抹平平臺差別性,而是 在語言層面作了平臺差別性抹平。也就是說你要開發IOS應用不只會Kotlin/Native還得會IOS 應用開發Development Api. 這貌似Kotlin/Native稍遜Flutter一籌,可是想一想也有道理,API層面抹平不是在語言層面職責,仍是須要框架層面來作到這一點,說不定哪天Kotlin團隊也造出一個相似Flutter的輪子呢。並且語言層面跨平臺帶來的還有一點好處就是共享代碼,Android、IOS、前端均可以用Kotlin來實現,能夠把它們擁有相同邏輯用Kotlin編寫的代碼放入一個common包中統一管理,而且Kotlin對多平臺共享這塊作了很好的支持,而後來自於不一樣平臺能夠共享這塊通用的邏輯實現,不用各自平臺去寫一套了。

三、給開發者抉擇建議:

經過上述觀點,咱們總結到對於開發者而言,學習一門新的技術實際上通常會存在兩層隱造成本。一層是對新的開發語言的掌握,另外一層則是對這門技術的Development Api的熟悉和掌握。那麼Kotlin/Native就是爲了磨平第一層開發語言的障礙。語言層面能作的也只能這樣了,不像Flutter它是一個框架,它能夠直接從API層面抹平。若是Flutter最終能推廣起來,在熱更新和熱修復這塊支持比如今的原生還好的話,而且你是一個初學者,那麼它絕對是一個不錯的選擇。

若是你是Kotlin開發者,你徹底可使用Kotlin/Native而後花一些時間熟悉下IOS的API也能把IOS應用玩起來。固然Kotlin/Native開發IOS應用只是其中一小部分,你徹底用它去作更多有意義的事。

若是你是移動開發者,建議兩門技術均可以去嘗試一下畢竟技多不壓身,固然語言只是一門工具,最終靠仍是計算機紮實的基礎、分析需求的能力以及架構項目的功能能力。有了這些能力再加上一個高效的編程語言那麼你就更加所向披靡了。

Kotlin系列文章,歡迎查看:

原創系列:

翻譯系列:

實戰系列:

歡迎關注Kotlin開發者聯盟,這裏有最新Kotlin技術文章,每週會不按期翻譯一篇Kotlin國外技術文章。若是你也喜歡Kotlin,歡迎加入咱們~~~

相關文章
相關標籤/搜索