赞
踩
简述: 今天我们来讲点Kotlin中比较时髦的东西,有的人可能会说:“不像你之前的风格啊,之前的文章不是一直在死扣语法以及语法糖背后秘密。当你还在死扣泛型语法的时候,别人的文章早就说了Kotlin/Native和Kotlin1.3的新特性”。瞬间感觉自己out了,今天我们就说说这些时髦的东西,也许你能看到一些和别人不一样的东西哦。
前段时间你们的熊猫小哥哥(也就是我),由于对Kotlin过度热爱,一天偶然看到2018 JetBrains开发者日-Kotlin专场活动,脑袋一热,瞬间心动了,马上就买了门票和火车票去北京(第一次一个人去北京)参加活动了。因为看到有Kotlin中文社区两位大佬(这两位大佬是我一年多以前开始写Kotlin的时候就关注了他们)的演讲日程以及JetBrains资深布道师Hali的演讲,没有过多思考直接买票,不要怂就是干。最后顺便和Kotlin社区的大佬们面个基啥的,谢谢大佬们的热情款待。此次北京之行收获挺多的,有时候知道一些最新技术方向和动态会比你埋头闭门造车好的很多。
因为在我的公众号上(Kotlin开发者联盟),有一些小伙伴希望我能从北京的开发者会上带点东西回来,所以总结了一下结合自己实际的开发,给大家带来以下几篇文章。
那么,今天就开始第一篇,看过一些大佬写关于Kotlin/ Native的文章,基本上都是翻译了Kotlin Blog的官网博客, 具体如何实践的还是比较少的。今天我不打算这么讲,既然今天的主题是时髦那就讲点有意思的东西。一起来看下今天提纲:
在开始之前,我觉得有必要一起重新来认识一下Kotlin这门语言,很多人一直都认为它不就是门JVM语言和Java、Scala一样都是跑在JVM虚拟机上。其实Kotlin并不仅仅是一门JVM语言,它的野心是真的大,JVM语言已经无法满足它的雄心壮志了。它是一门多平台的静态编译型语言,它可以用于JVM上(只不过在JVM层面比较出名而已,导致很多人都认为它是门JVM语言),实则它可以编译成JavaScipt运行在浏览器中也可以编译成IOS的可运行文件跑在LLVM上
用官方的话来说Kotlin / Native是一种将Kotlin代码编译为本机二进制文件的技术,可以在没有虚拟机的情况下运行。它是基于LLVM的后端,用于Kotlin编译器和Kotlin标准库的本机实现。
Kotlin/Native目前支持以下平台:
为了更好说明Kotlin/Native能力,下面给出张官方的Kotlin/Native能力图:
对于Kotlin/Native之前一直没有去玩过,只是经常听到社区小伙伴们说编译起来巨慢,感觉好时髦啊。抱着好奇心,并且也符合我们这篇文章时髦的主题,决定一步步带大家玩一玩。
1、需要准备的开发工具
2、创建一个Kotlin/Native项目
第一步: 选择左侧的Kotlin/Native, 并选择右侧的Sing View App with a Kotlin/Native Framework
第二步: 填写项目名和包名,选择语言Swift(这里先以Swift为例)
第三步: 最后finish即可创建完毕Kotlin/Native项目,创建完毕后项目结构如下
4、运行Kotlin/Native项目
如果你比较幸运跑起来的话,效果应该是在模拟器装一个APP并且起了一个空白页,终端上输出了"Hello from Kotlin!"的Log,类似这样:
注意: 但是你是真题测试,而且Run顶部默认只有一个IOS Device选项的话,然后你又点了Run 说明而且会报如下错误
这个问题是因为默认IOS Device选项是表示用真机调试哈,然后这边就需要一个IOS开发者账号。设置开发者账号的话,建议使用Xcode去打开该项目然后给该项目配置一个开发者账号。
设置完毕Xcode后,AppCode会自动检测到刷新的。
看到上面IOS HelloWorld项目运行起来,大家有没有思考一个问题,Kotlin的代码的代码是怎么在IOS设备上跑起来呢?
实际上,在这背后使用了一些脚本和工具在默默支撑着整个项目的运行,如前所述,Kotlin / Native平台有自己的编译器,但每次想要构建项目时手动运行它明显不是高效的。 所以Kotlin团队了选择Gradle。Kotlin / Native使用Gradle构建工具在Xcode中自动完成Kotlin / Native的整个构建过程。在这里使用Gradle意味着开发人员可以利用其内部增量构建架构,只需构建和下载所需内容,从而节省开发人员的宝贵时间。
如果,你还对上述有点疑问不妨一起来研究下Kotlin/Native项目中的构建参数脚本:
通过以上项目可以分析到在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')
- }
1、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一遍,效果如下:
2、Kotlin/Native + Objective C项目结构分析
在IOS同事帮助下,进一步了解IOS APP启动基本知识,这将有助于我们接下来改造我们项目结构,使得它更加简单,完全可以删除额外的Swift代码,包括APP启动代理那块都交由Kotlin来完成。
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类
- }
-
- }
- - (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;
- }
3、Kotlin/Native + Kotlin项目结构分析
到这里很多人就会问了,看你上面说了那么并没有看到你Kotlin在做什么事,全是Swift和OC在做APP启动。现在就是告诉你Kotlin如何去替代它们做APP启动的事了。
- 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
- }
- }
- }
- 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)会报错
- }
- 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}!'"
- }
- }
运行出来的效果如下:
1、IOS项目ViewController与组件绑定过程分析
看到上面的运行Demo,大家有没有在思考一个问题IOS项目中的ViewController是怎么和UI组件绑定在一起的呢?我个人认为这个很重要,换句话说这就是IOS开发最基本的套路,如果这个都不弄明白的话,下面Demo开发就是云里雾里了,掌握了这个基本套路的话,作为一个Android开发者,你基本上就可以在IOS项目开发中任意折腾了。
- //导入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)
- }
- }
- }
第五步: 右击组件MKMapView可以看到黑色对话框,里面Referencing Outlets还空的,说明当前ViewController没有和MKMapView组件绑定
第六步: 配置outlet,这里说下AppCode很坑爹地方,需要手动去source code中手动配置outlet,选中main.storyboard右击open as 然后选择打开source code
- <connections>
- <outlet property="mMapView" destination="dest id" id="generate id"/>
- </connections>
- <!--property属性值就是KNMapViewController中的mMapView变量名;destination属性值是一个map view标签中id(可以在subviews标签内的mapView标签中找到id), id属性则是自动生成的,可以按照格式自己之指定一个,只要不出现重复的id即可-->
配置结果如下:
2、接着上述配置步骤,就可以回到AppCode中运行项目了
以上的运行结果就说明了,我们Demo已经运行成功了。并且我已经把此次Kotlin/Native项目都放到了GitHub上,如果感兴趣的小伙伴可以clone下来玩一玩这个时髦的鬼东西。如果觉得对你有帮助,还请大佬们给个star。
Kotlin/Native目前还是处于1.0的beta,所以还是有很多的地方是不让人满意的。下面我总结这次Kotlin/Native开发体验的优缺点:
优点:
通过上述的几个例子,可以明显表明Kotlin/Native语言层面跨平台能力还是很强的,和OC,Swfit项目操作性也很强,该有的基本上都已经实现了,所以对于它后续发展还是非常值得关注的。据我了解到,Kotlin团队目前首要重心就是在Kotlin/Native这一块,希望他们能给我们带来更多关于Kotlin/Native的惊喜。
缺点
缺点还是有很多的:
最后可以测试一下:
1、简述:
其实关于这个主题,我是不太想去说的,因为我不想引起编程语言界的口战。但是总有人喜欢去把Kotlin/Native和Flutter放在一起去做对比,因为他们好像都有一个共同点就是跨平台都能开发Android、IOS应用。并且在此次Jetbrains开发者日上就有参会嘉宾在会上问官方布道师Hali,Kotlin/Native和Flutter有什么不一样,优势在哪?
2、提出个人观点:
针对这个问题我说下个人的观点:
首先,我个人是非常看好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对多平台共享这块做了很好的支持,然后来自于不同平台可以共享这块通用的逻辑实现,不用各自平台去写一套了。
3、给开发者抉择建议:
通过上述观点,我们总结到对于开发者而言,学习一门新的技术实际上一般会存在两层隐形成本。一层是对新的开发语言的掌握,另一层则是对这门技术的Development Api的熟悉和掌握。那么Kotlin/Native就是为了磨平第一层开发语言的障碍。语言层面能做的也只能这样了,不像Flutter它是一个框架,它可以直接从API层面抹平。如果Flutter最终能推广起来,在热更新和热修复这块支持比现在的原生还好的话,并且你是一个初学者,那么它绝对是一个不错的选择。
如果你是Kotlin开发者,你完全可以使用Kotlin/Native然后花一些时间熟悉下IOS的API也能把IOS应用玩起来。当然Kotlin/Native开发IOS应用只是其中一小部分,你完全用它去做更多有意义的事。
如果你是移动开发者,建议两门技术都可以去尝试一下毕竟技多不压身,当然语言只是一门工具,最终靠还是计算机扎实的基础、分析需求的能力以及架构项目的功能能力。有了这些能力再加上一个高效的编程语言那么你就更加所向披靡了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。