微信团队分享:Kotlin渐被认可,Android版微信的技术尝
本文由微信开发团队工程是由“oneliang”原创发表于WeMobileDev公众号,内容稍有改动。
1、引言
Kotlin 是一个用于现代多平台应用的静态编程语言,由 JetBrains 开发(也就是开发了号称Java界最智能的集成开发工具IntelliJ IDEA的公司)。Kotlin可以编译成Java字节码(就像Groovy和Scala一样),也可以编译成JavaScript,方便在没有JVM的设备上运行。Kotlin已于2017年的Google I/O开发者大会上正式被宣布为Android官方支持开发语言(见《[资讯] Kotlin成为Android官方开发语言!》)。
有人说Kolin对于Android的作用,是不是Swift对于iOS的作用一样(主要用于降低Objective-C开发门槛等)。实际上,Kotlin对于Android的意义和重要性要远大于Swift对于iOS,因为不管是Objective-C还是Swift,它们至少都是苹果自已的东西,而悲剧的是Java并不属于Google。鉴于Google和Oracle(Java的创造者SUN公司早就被Oracle收购了)的官司(见《[资讯] Java侵权案逆转:Google需赔88亿!》),如何解决掉Java这个如鲠在喉的历史遗留,是Android决策者早就在考虑的问题,只是恰好选中了Kotlin而已。
Google官方已在各种场合直接或间接地表明了对于Kotlin和Java的态度——那就是Kotlin是 “Over” Java的(即可以理解为Kotlin在ANdroid中的定位是高于Java的)。所以,不管Android开发者有没有做好准备,或者还在纠结要不要学习Kotlin时,都不影响Kotlin在Android中的定位和越来越明确的地位。但无论如何,对于Android开发者来说,多学一门技术确实很痛苦,但提前做好准备是更明智之选,至少到了Kotlin真的取代Java的那一天,而不至于后懂准备地太晚。
作为移动端即时通讯IM应用的王者——微信,为了始终保持技术的领先性,无论日后Kotlin在微信客户中的重要性几何,技术团队做好技术储备和预研实践是肯定有必要的,于是便有了本文的整理和分享,希望业界共同学习、互相交流。
(本文同步发布于:http://www.52im.net/thread-2066-1-1.html)
2、概述
微信订阅号助手的Android App项目首次尝试使用Kotlin进行大规模的业务开发(483个Kt文件,3.8W行不包含空行的Kt代码),一开始接触Kotlin的时候难免会有点不适应,但经过几天的强制使用后,慢慢有些感觉,项目落地后回顾了一下,发现Kotlin确实是有它独特的风味。
什么是微信订阅号助手?
微信公众平台“订阅号助手”APP已正式上架App Store,通过这款订阅号助手APP,公众号运营者可以快捷地编辑和发表内容、方便地处理留言和回复粉丝消息。
订阅号助手app能将你的iPhone变成一个随身的公众号“工作室”,无论身处何地,你都可以发表内容、与读者互动。订阅号助手app简洁的编辑工具让每个人轻松变身为作者,留住即刻的灵感,尽享内容创作的乐趣。订阅号助手app让每个有才华的个体都有机会被关注,都有自己的品牌。
3、“烹饪”准备
食材:
1)Android,主要食材(指Framework、Api等);
2)Kotlin,食用安全、味鲜(扩展函数)、香(重载)、甜(富含糖份Lambda),第二主要食材,切好块状;
3)Java,少量,Kotlin这种食材需要它来做引子。
锅:
AndroidStudio、Eclipse这两个牌子的锅质量都不错。
调味料:
Kotlin Android Extension、Android KTX、AndroidX、Anko等。
如果没有上述这些材料请移步到如下网址"购买":
https://developers.google.com/android
https://kotlinlang.org/docs/reference
4、“烹饪”过程
1)开火,放少量食用油;
2)先把Android倒进去,伴两下;
3)倒少量Java,主要是"字节码"和"工具部分",再伴两下;
4)把切好块的Kotlin一块块慢慢平铺在Android上面,把Android盖住;
5)慢火煮3-5分钟,观察一下这个过程:
Kotlin把Android的味道慢慢释放出来,比Android + Java更香;
Kotlin与Java融为一体 (前提是少量Java,如果Java放得太多,香味会受影响,粘合不够好,容易松散(NPE));
6)关火,焖一会。
5、开锅,上菜
色香味倶全,敬请尽情享受这番独特的风味。
5.1 特色风味一:食用安全
食用安全,Nullable or NotNul从源头抓起。
Kotlin代码安全性更强:
varoutput: String
output = null// Compilation error
val name: String? = null// Nullable type
println(name.length()) // Compilation error
食用安全从从源头上抓起,只要跟定义不符就编译不通过,这是Kotlin小而精的一个优点,一下子能把整碟"菜"的安全系数提高,此Code来自官方文档。
5.2 特色风味二:鲜
扩展函数,味道鲜美,百吃不厌。
项目工具类的另一种写法:
fun String.toIntSafely(defaultValue: Int = 0): Int {
returntry{
this.toInt()
} catch(e: Exception) {
defaultValue
}
}
fun main(args: Array<String>) {
println("1".toIntSafely())
}
String 转 Int,这种需求几乎很多项目都是需要,像上述Kotlin如果是在Java里面描述的话,估计会写成这样:
public final class StringUtil{
private StringUtil() {}
public static int stringToInt(String string, int defaultValue) {
//省略
}
}
使用时:
StringUtil.stringToInt("1", 0);
大家看到这里可能会觉得没什么,大家都是工具类,用的时候有些小差别而已。
但正因为这些小差别,优点就体现出来了,确实是鲜美:
1)不需要记住工具类的名字和方法的名字:假如你是一个刚接手项目的新人,正准备做一个需求开发,突然需要这种String to Int的工具,但是不知道工具在哪,这就好比你去到一个陌生人的家里,想找个螺丝刀拧个松掉的螺丝一样,这“螺丝刀”在哪?除了问“主人”之外,要么就是“翻柜子”,这不就显得效率低么?使用Kotlin的扩展函数就能有效避免前面所说的问题,接手项目的新人只需要轻轻的“.”一下,滚两下鼠标,"toIntSafely"的方法就会看到。这就为什么你看Kotlin的Java扩展库很多都是通过扩展函数来封装;
2)方法的类归属更好理解:以上述的"toIntSafely"为例,String.toIntSafely,使得开发者更容易直观感受到这个函数是用于String,不像StringUtil.stringToInt没有归属可言,纯粹就是一个工具函数,不如Kotlin的写法容易理解;
3)对定义函数者的要求高了:正因体现了函数的类归属,也就使得开发者在定义函数的时候需要考虑归属给哪个类还是顶层函数这些问题,归属的范围少了,会导致不好用,范围广了又怕暴露导致滥用或者误用。
5.3 特色风味三:香
重载(Overload),回味无穷。
虽然这个概念在面向对象领域用得很多,但Kotlin这个重载的味道真是令我们吃上瘾。
重载在工具类的场景用得非常多,一个项目下来没工具类也是不可能。
例如我们在项目中会封装一些对话框(Dialog)工具类供开发的同学一句调用:
1)开发的同学需要在界面显示一个Dialog,只想改变Dialog的内容,那么Java里面就有showDialog(String message)的写法;
2)开发的同学需要在界面显示一个Dialog,即想改变Dialog的标题,又想改变Dialog的内容,那么Java里面就有showDialog(String title, String message)的写法;
3)开发的同学想改变Dialog里面Icon的....
4)开发的同学想......
这些场景估计做Android开发的同学都会碰到,其实不限于Android,Java开发的同学也经常遇到。
我们看看Kotlin是怎样把这些需求收拢:
fun showDialog(title: String = "标题", message: String = "内容") {
//TODO
}
这个写法一下子满足 2的2次方(4) 种重载方法:
showDialog()
showDialog("新标题")
showDialog(message = "新内容")
showDialog("新标题", "新内容")
这种重载方式有效地减少我们项目中的重载方法数量,使得我们项目开发更简洁和更有效率 ,自然就回味无穷。
5.4 特色风味四:甜而不腻
带了糖,甜而不腻。
Kotlin里面Function与Lambda既可相互理解,又有其味道(写法)上的一些差异。
味道 (结果) 一样,但味道消去的过程 (用法) 有差别。
Function(函数)常用写法:
fun f(x: Int): Any {
returnAny()
}
用法:
val y = f(1)
Function(函数)的一种Lambda写法:
fun f() = { x: Int -> Any() } 等价于 fun f(): (Int) -> Any = { x: Int -> Any() }
用法:
val y = f()(1) 或 val y = f().invoke(1)
Lambda写法:
val f = { x: Int -> Any() } 等价于 val f: (Int) -> Any = { x: Int -> Any }
用法:
val y = f(1) 或 val y = f.invoke(1)
细节点:Function时,有"="跟没有"="意义不一样,有"="的时候可以理解右边( { x: Int -> Any() } )是 左边函数返回类型((Int) -> Any) 的实现。
函数不用置疑,项目里面必备。
Lambda:
Lambda,语法糖,这是怎样的一种成份?
Lambda是长这样的:
val block: () -> Unit = {}
val sum: (Int, Int) -> Int = { p1, p2 -> p1 + p2 }
Lambda令我们的项目减少了很多接口类,尤其是回调接口,我们项目几乎没有。一般的业务场景里面回调接口都会用得不少,Lambda能有效减少这种Callback接口的定义,少写不少接口类,事半功倍。
另lambda里面不能写return,最后一行的值就是返回值。
从数学函数角度抽象理解:
函数: y = f(x)
〉假设x与y都是Int类型
可以理解为 Kotlin 函数:
fun f(x: Int): Int {
return1 // 这里的返回值就是对应y
}
也可以理解为 Lambda:
val f = { x: Int -> 1 } 等价于 val f: (Int) -> Int = { x: Int -> 1 }
使用时f(1),但是如果像上述那种f(x)的kotlin函数与f(x)的lambda同时同名同方法签名存在,使用上要f(1)与f.invoke(1)来区分是函数调用还是lambda调用。
〉假设x与y都是Lambda类型
x是Lambda类型 (Int) -> Int ,y是Lambda类型 (Int) -> Int,可以换算成:
fun f(x: (Int) -> Int): (Int) -> Int {
return{ it -> x(it) }
}
或这样:
fun f(x: (Int) -> Int): (Int) -> Int = { it -> x(it) }
使用时:
f { it -> it + 10 }(1) or f { it -> it + 10 }.invoke(1)
或 Lambda:
val f: ((Int) -> Int) -> ((Int) -> Int) = { x -> { it -> x(it) } } // val时要inline
使用时:
f.invoke { it -> it + 10 }.invoke(1)
通过上述的 替换 能更好地理解和使用Lambda。
6、如何更好地了解Kotlin这种食材的味道
Kotlin用于Java领域,中间产物毫无疑问还是字节码,因此本质还是Java的基础知识,反编译Kotlin生成的字节码是学习Kotlin一种较好的方式,可利用AndroidStudio的Tools来反编译kt,能帮助快速理解Kotlin。
谢谢品尝这份美味,希望Kotlin这款食材能带给各位读者不少Android上的特色的风味。
附录:QQ、微信团队原创技术文章
《腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(图片压缩篇)》
《腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(音视频技术篇)》
《腾讯技术分享:Android版手机QQ的缓存监控与优化实践》
《微信团队分享:iOS版微信的高性能通用key-value组件技术实践》
《微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?》
《腾讯技术分享:Android手Q的线程死锁监控系统技术实践》
《QQ音乐团队分享:Android中的图片压缩技术详解(上篇)》
《QQ音乐团队分享:Android中的图片压缩技术详解(下篇)》
《腾讯团队分享 :一次手Q聊天界面中图片显示bug的追踪过程分享》
《微信团队分享:微信Android版小视频编码填过的那些坑》
《微信团队披露:微信界面卡死超级bug“15。。。。”的来龙去脉》
《月活8.89亿的超级IM微信是如何进行Android端兼容测试的》
《微信客户端团队负责人技术访谈:如何着手客户端性能监控和优化》
《微信团队原创分享:Android版微信的臃肿之困与模块化实践之路》
《微信团队原创分享:微信客户端SQLite数据库损坏修复实践》
《腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率》
《腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(下篇)》
《腾讯原创分享(三):如何大幅压缩移动网络下APP的流量消耗(上篇)》
《如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源》
《开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石 [源码下载]》
《微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解》
《微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)》
《微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)》
《Android版微信从300KB到30MB的技术演进(PPT讲稿) [附件下载]》
《微信团队原创分享:Android版微信从300KB到30MB的技术演进》
《微信技术总监谈架构:微信之道——大道至简(PPT讲稿) [附件下载]》
《微信海量用户背后的后台系统存储架构(视频+PPT) [附件下载]》
《微信异步化改造实践:8亿月活、单机千万连接背后的后台解决方案》
《架构之道:3个程序员成就微信朋友圈日均10亿发布量[有视频]》
《微信团队原创分享:Android内存泄漏监控和优化技巧总结》
《微信团队原创Android资源混淆工具:AndResGuard [有源码]》
《移动端IM实践:Android版微信如何大幅提升交互性能(一)》
《移动端IM实践:Android版微信如何大幅提升交互性能(二)》
《移动端IM实践:WhatsApp、Line、微信的心跳策略分析》
《移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)》
《信鸽团队原创:一起走过 iOS10 上消息推送(APNS)的坑》
《腾讯TEG团队原创:基于MySQL的分布式数据库TDSQL十年锻造经验分享》
《微信多媒体团队访谈:音视频开发的学习、微信的音视频技术和挑战等》
《了解iOS消息推送一文就够:史上最全iOS Push技术详解》
《腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面》
《腾讯音视频实验室:使用AI黑科技实现超低码率的高清实时视频聊天》
《腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践》
《手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)》
《微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)》
《微信技术分享:微信的海量IM聊天消息序列号生成实践(容灾方案篇)》
《腾讯技术分享:GIF动图技术详解及手机QQ动态表情压缩技术实践》
《微信团队分享:Kotlin渐被认可,Android版微信的技术尝鲜之旅》
>> 更多同类文章 ……
(本文同步发布于:http://www.52im.net/thread-2066-1-1.html)