为数不多的人知道的 Kotlin 技巧及解析(三)
本文没有什么奇淫技巧,都是一些在实际开发中常用的技巧
Google 引入 Kotlin 的目的就是为了让 Android 开发更加方便,自从官宣 Kotlin 成为了 Android 开发的首选语言之后,已经有越来越多的团队,在项目使用 Kotlin。
众所周知 xml 十分耗时,因此在 Android 10.0 上新增加 tryInflatePrecompiled
方法,这是一个在编译期运行的一个优化,因为布局文件越复杂 XmlPullParser 解析 XML 越耗时, tryInflatePrecompiled
方法根据 XML 预编译生成 compiled_view.dex
, 然后通过反射来生成对应的 View,从而减少 XmlPullParser 解析 XML 的时间,但是目前一直处于禁用状态。源码解析请查看 Android 资源加载源码分析一。
因此一些体量比较大的应用,为了极致的优化,缩短一点时间,对于简单的布局,会使用 Kotlin 去重写这部分 UI,但是门槛还是很高,随着 Jetpack Compose 的出现,其目的是让您更快、更轻松地构建原生 Android 应用,前不久 Google 正式发布了 Jetpack Compose 1.0。
Kotlin 优势已经体现在了方方面面,结合着 Kotlin 的高级函数的特性可以让代码可读性更强,更加简洁,但是如果使用不当会对性能造成一些损耗,更多内容可前往查看。
- 为数不多的人知道的 Kotlin 技巧及解析(二)
- 为数不多的人知道的 Kotlin 技巧及解析(一)
以上两篇文章,主要分享了 Kotlin 在实际项目中使用的技巧,以及如果使用不当会对 性能 和 内存 造成的那些影响以及如何规避这些问题等等。
通过这篇文章你将学习到以下内容:
- 什么是 Contract,以及如何使用?
- Kotlin 注解在项目中的使用?
- 一行代码接受 Activity 或者 Fragment 传递的参数?
- 一行代码实现 Activity 之间传递参数?
- 一行代码实现 Fragment 之间传递参数?
- 一行代码实现点击事件,避免内存泄露?
KtKit 仓库
这篇文章主要围绕一个新库 KtKit 来介绍一些 Kotlin 技巧,正如其名 KtKit 是用 Kotlin 语言编写的工具库,包含了项目中常用的一系列工具,是 Jetpack ktx 系列的补充,涉及到了很多从 Kotlin 源码、Jetpack ktx、anko 等等知名的开源项目中学习到的技巧,包含了 Kotlin 委托属性、高阶函数、扩展函数、内联、注解的使用等等。
- KtKit 仓库地址:https://github.com/hi-dhl/KtKit
- KtKit 在线阅读:https://ktkit.hi-dhl.com
如果想要使用文中的 API 需要将下列代码添加在模块级 build.gradle
文件内, 最新版本号请查看 版本记录。
implementation "com.hi-dhl:ktkit:${ktkitVersion}"
因为篇幅原因,文章中不会过多的涉及源码分析,源码部分将会在后续的文章中分享。
什么是 Contract,以及如何使用
众所周知 Kotlin 是比较智能的,比如 smart cast 特性,但是在有些情况下显得很笨拙,并不是那么智能,如下所示。
public inline fun String?.isNotNullOrEmpty(): Boolean {return this != null && !this.trim().equals("null", true) && this.trim().isNotEmpty()
}fun testString(name: String?) {if (name.isNotNullOrEmpty()) {println(name.length) // 1}
}
正如你所见,只有字符串 name 不为空时,才会进入注释 1 的地方,但是以上代码却无法正常编译,如下图所示。
编译器会告诉你一个编译错误,经过代码分析只有当字符串 name 不为空时,才会进入注释 1 的地方,但是编译器却无法正常推断出来,真的是编译器做不到吗?看看官方文档是如何解释的。
However, as soon as these checks are extracted in a separate function, all the smartcasts immediately disappear:
将检查提取到一个函数中, smart cast 所带来的效果都会消失
编译器无法深入分析每一个函数,原因在于实际开发中我们可能写出更加复杂的代码,而 Kotlin 编译器进行了大量的静态分析,如果编译器去分析每一个函数,需要花费时间分析上下文,增加它的编译耗时的时间。
如果要解决上诉问题,这就需要用到 Contract 特性,Contract 是 Kotlin 提供的非常有用的特性,Contract 的作用就是当 Kotlin 编译器没有足够的信息去分析函数的情况的时候,Contracts 可以为函数提供附加信息,帮助 Kotlin 编译器去分析函数的情况,修改代码如下所示。
inline fun String?.isNotNullOrEmpty(): Boolean {contract {returns(true) implies (this@isNotNullOrEmpty != null)}return this != null && !this.trim().equals("null", true) && this.trim().isNotEmpty()
}fun testString(name: String?) {if (name != null && name.isNotNullOrEmpty()) {println(name.length) // 1}
}
相比于之前的代码,在 isNotNullOrEmpty()
函数中添加了 contract 代码块即可正常编译通过,这行代码的意思就是,如果返回值是 true ,this 所指向对象就不为 null。 而在 Kotlin 标准库中大量的用到 contract 特性。 上述示例的使用可前往查看 KtKit/ProfileActivity.kt。
Kotlin 注解在项目中的使用
contract 是 Kotlin 1.3 添加的实验性的 API,如果我们调用实验性的 API 需要添加 @ExperimentalContracts
注解才可以正常使用,但是如果添加 @ExperimentalContracts
注解,所有调用这个方法的地方都需要添加注解,如果想要解决这个问题。只需要在声明 contract 文件中的第一行添加以下代码即可。
@file:OptIn(ExperimentalContracts::class)
在上述示例中使用了 inline 修饰符,但是编译器会有一个黄色警告,如下图所示。
编译器建议我们将函数作为参数时使用 Inline,Inline (内联函数) 的作用:提升运行效率,调用被 inline 修饰符的函数,会将方法内的代码段放到调用处。
既然 Inline 修饰符可以提升运行效率,为什么还给出警告,因为 Inline 修饰符的滥用会带来性能损失,更多内容前往查看 Inline 修饰符带来的性能损失。
Inline 修饰符常用于下面的情况,编译器才不会有警告:
- 将函数作为参数(例如:lambda 表达式)
- 结合 reified 实化类型参数一起使用
但是在普通的方法中,使用 Inline 修饰符,编译会给出警告,如果方法体的代码段很短,想要通过 Inline 修饰符提升性能(虽然微乎其微),可以在文件的第一行添加下列代码,可消除警告。
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
然后在使用 Inline 修饰符的地方添加以下注解,即可愉快的使用。
@kotlin.internal.InlineOnly
注解 @kotlin.internal.InlineOnly
的作用:
- 消除编译器的警告
- 修改内联函数的可见性,在编译时修改成 private
// 未添加 InlineOnly 编译后的代码
public static final void showShortToast(@NotNull Context $this$showShortToast, @NotNull String message) {......Toast.makeText($this$showShortToast, (CharSequence)message, 0).show();
}// 添加 InlineOnly 编译后的代码
@InlineOnly
private static final void showShortToast(Context $this$showShortToast, String message) {......Toast.makeText($this$showShortToast, (CharSequence)message, 0).show();
}
关于注解完整的使用案例,可前往仓库 KtKit 查看。
一行代码接受 Activity 或者 Fragment 传递的参数
如果想要实现一行代码接受 Activity 或者 Fragment 传递的参数,可以通过 Kotlin 委托属性来实现,在仓库 KtKit 中提供了两个 API,根据实际情况使用即可。案例可前往查看 KtKit/ProfileActivity.kt。
class ProfileActivity : Activity() {// 方式一: 不带默认值private val userPassword by intent<String>(KEY_USER_PASSWORD)// 方式二:带默认值:如果获取失败,返回一个默认值private val userName by intent<String>(KEY_USER_NAME) { "公众号:ByteCode" }
}
一行代码实现 Activity 之间传递参数
这个思路是参考了 anko 的实现,同样是提供了两个 API , 根据实际情况使用即可,可以传递 Android 支持的任意参数,案例可前往查看 KtKit/ProfileActivity.kt。
// API:
activity.startActivity<ProfileActivity> { arrayOf( KEY_USER_NAME to "ByteCode" ) }
activity.startActivity<ProfileActivity>( KEY_USER_NAME to "ByteCode" )// Example:
class ProfileActivity : Activity() {......companion object {......// 方式一activity.startActivity<ProfileActivity> {arrayOf(KEY_USER_NAME to "ByteCode",KEY_USER_PASSWORD to "1024")}// 方式二activity.startActivity<ProfileActivity>( KEY_USER_NAME to "ByteCode",KEY_USER_PASSWORD to "1024" )}
}
Activity 之间传递参数 和 并回传结果
// 方式一
context.startActivityForResult<ProfileActivity>(KEY_REQUEST_CODE,KEY_USER_NAME to "ByteCode",KEY_USER_PASSWORD to "1024"
)// 方式二
context.startActivityForResult<ProfileActivity>(KEY_REQUEST_CODE) {arrayOf(KEY_USER_NAME to "ByteCode",KEY_USER_PASSWORD to "1024")
}
回传结果
// 方式一
setActivityResult(Activity.RESULT_OK) {arrayOf(KEY_RESULT to "success",KEY_USER_NAME to "ByteCode")
}// 方式二
setActivityResult(Activity.RESULT_OK,KEY_RESULT to "success",KEY_USER_NAME to "ByteCode"
)
一行代码实现 Fragment 之间传递参数
和 Activity 一样提供了两个 API 根据实际情况使用即可,可以传递 Android 支持的任意参数,源码前往查看 KtKit/LoginFragment.kt。
// API:
LoginFragment().makeBundle( KEY_USER_NAME to "ByteCode" )
LoginFragment().makeBundle { arrayOf( KEY_USER_NAME to "ByteCode" ) }// Example:
class LoginFragment : Fragment(R.layout.fragment_login) {......companion object {......// 方式一fun newInstance1(): Fragment {return LoginFragment().makeBundle(KEY_USER_NAME to "ByteCode",KEY_USER_PASSWORD to "1024")}// 方式二fun newInstance2(): Fragment {return LoginFragment().makeBundle {arrayOf(KEY_USER_NAME to "ByteCode",KEY_USER_PASSWORD to "1024")}}}
}
一行代码实现点击事件,避免内存泄露
KtKit 提供了常用的三个 API:单击事件、延迟第一次点击事件、防止多次点击
单击事件
view.click(lifecycleScope) { showShortToast("公众号:ByteCode" }
延迟第一次点击事件
// 默认延迟时间是 500ms
view.clickDelayed(lifecycleScope){ showShortToast("公众号:ByteCode" }// or
view.clickDelayed(lifecycleScope, 1000){ showShortToast("公众号:ByteCode") }
防止多次点击
// 默认间隔时间是 500ms
view.clickTrigger(lifecycleScope){ showShortToast("公众号:ByteCode") }// or
view.clickTrigger(lifecycleScope, 1000){ showShortToast("公众号:ByteCode") }
但是 View#setOnClickListener
造成的内存泄露,如果做过性能优化的同学应该会见到很多这种 case。
根本原因在于不规范的使用,在做业务开发的时候,根本不会关注这些,那么如何避免这个问题呢,Kotlin Flow 提供了一个非常有用的 API callbackFlow
,源码如下所示。
fun View.clickFlow(): Flow<View> {return callbackFlow {setOnClickListener {safeOffer(it)}awaitClose { setOnClickListener(null) }}
}
callbackFlow
正如其名将一个 callback 转换成 flow,awaitClose
会在 flow 结束时执行。
那么 flow 什么时候结束执行
源码中我将 Flow 通过 lifecycleScope 与 Activity / Fragment 的生命周期绑定在一起,在 Activity / Fragment 生命周期结束时,会结束 flow , flow 结束时会将 Listener 置为 null,有效的避免内存泄漏,源码如下所示。
inline fun View.click(lifecycle: LifecycleCoroutineScope, noinline onClick: (view: View) -> Unit) {clickFlow().onEach {onClick(this)}.launchIn(lifecycle)
}
总结
仓库 KtKit 是用 Kotlin 语言编写的工具库,包含了项目中常用的一系列工具,但是目前还不是很完善,正在陆续将一些常用的功能,结合着 Kotlin 的高级函数的特性,不仅让代码可读性更强,使用更加简单,而且还可以帮助我们解决项目中常见的问题。
- KtKit 仓库地址:https://github.com/hi-dhl/KtKit
- KtKit 在线阅读:https://ktkit.hi-dhl.com
项目中引用了 spotless 插件,执行 ./gradlew spotlessApply
会将 Java 、Kotlin 、xml 、gradle 、md 、gitignore 等等文件按照官方标准去格式化代码。这也是 Google 提交代码的时候,推荐的方式。
全文到这里就结束了,如果这个仓库对你有帮助,请在仓库右上角帮我 star 一下,非常感谢你的支持,同时也欢迎你提交 PR ❤️❤️❤️
如果有帮助 点个赞 就是对我最大的鼓励
代码不止,文章不停
最后推荐我一直在更新维护的项目和网站:
个人博客,将所有文章进行分类,欢迎前去查看 https://hi-dhl.com
计划建立一个最全、最新的 AndroidX Jetpack 相关组件的实战项目 以及 相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,欢迎前去查看:AndroidX-Jetpack-Practice
LeetCode / 剑指 offer / 国内外大厂面试题 / 多线程 题解,语言 Java 和 kotlin,包含多种解法、解题思路、时间复杂度、空间复杂度分析
剑指 offer 及国内外大厂面试题解:在线阅读
LeetCode 系列题解:在线阅读
最新 Android 10 源码分析系列文章,了解系统源码,不仅有助于分析问题,在面试过程中,对我们也是非常有帮助的,仓库持续更新,欢迎前去查看 Android10-Source-Analysis
整理和翻译一系列精选国外的技术文章,每篇文章都会有译者思考部分,对原文的更加深入的解读,仓库持续更新,欢迎前去查看 Technical-Article-Translation
「为互联网人而设计,国内国外名站导航」涵括新闻、体育、生活、娱乐、设计、产品、运营、前端开发、Android 开发等等网址,欢迎前去查看 为互联网人而设计导航网站
为数不多的人知道的 Kotlin 技巧及解析(三)相关推荐
- 为数不多的人知道的 Kotlin 技巧以及原理解析 | 开发者说·DTalk
本文原作者: HiDhl,原文发布于: 掘金 https://juejin.im/post/6847902224467623950 文章中没有奇淫技巧,都是一些在实际开发中常用,但很容易被我们忽略的一 ...
- 为数不多的人知道的AndroidStudio快捷键(二)
为数不多的人知道的AndroidStudio快捷键(二) 这篇文章将会继续分享一些实用的快捷键,没有看过为数不多的人知道的AndroidStudio快捷键(一),可以点击下方连接前往 为数不多的人知道 ...
- androidstudio自动生成变量_为数不多的人知道的AndroidStudio快捷键(一)
好的idea能提高我们的工作效率,如果掌握一些快捷键,能帮我们从繁琐重复的工作解放出来,写起代码来效率就越高,接下来想分享一些实用的快捷键, 如果已经看完了这篇,点击下方连接前往为数不多的人知道的An ...
- 几个超级实用但很少人知道的 VS 技巧[更新]
大家好,今天分享几个我知道的实用 VS 技巧,而这些技巧我发现很多人都不知道.因为我经常在工作中遇到:我在同事电脑上解决问题,或在会议上演示代码示例时,使用了一些 VS "骚"操作 ...
- [css] 举例说明你知道的css技巧有哪些?
[css] 举例说明你知道的css技巧有哪些? /* 等比例容器 */ .ratio { position: relative; display: block; } .ratio:before { c ...
- android电视打开ppt,很少人知道的3种智能电视演示PPT的方法
原标题:很少人知道的3种智能电视演示PPT的方法 演示PPT,通常是在投影上,但随着大屏智能电视的火热和普及,越来越多的办公室以大屏智能电视取代投影,成为办公室不可或缺的生产力.今天就以60吋酷开K6 ...
- kdj值应用口诀_极少人知道的“KDJ”波段指标,学好5个口诀,离股神很近了
原标题:极少人知道的"KDJ"波段指标,学好5个口诀,离股神很近了 KDJ大家都知道,但是未必都深度了解和熟练应用,但是该指标在实战中成功率却名列前茅,而且更是做波段的强势神器.想 ...
- 8个老手都不一定知道的sketch技巧
Sketch老手都不一定知道的sketch小技巧 001.拖动和删除样式 让我们从一个简单的技巧开始.如果要删除样式属性(如模糊或填充),只需从中删除即可"检查器"把面板拖到画板里 ...
- c 语言boll型变量,极少人知道的BOLL指标使用技巧 一旦学会终身受益!
很多炒股的朋友都喜欢研究各种各样的指标和战法,殊不知研究多了,自己会乱,长远的投资,只要真正的掌握一个指标和战法,用以极致,那么也是能够稳定获利的,我知道很多老股民朋友喜欢用BOLL这个指标来帮助自己 ...
最新文章
- 威纶通宏开机后使用初始化宏指令_【操作系统】我们按下电脑开机键的背后发生了什么?...
- 了解你所不知道的SMON功能(十二):Shrink UNDO(rollback) SEGMENT
- 基于主成分分析与支持向量机的人脸识别
- SAP Fiori应用里日期格式的显示奥秘
- python3多线程编程_Python 3-多线程编程
- CGGeometry.h详解
- python3.6安装步骤-Ubuntu16.04安装python3.6详细教程
- MacOS:Shell工具-Royal TSX
- 怎样固定计算机桌面背景,Win7桌面背景老是被修改如何将其锁定不让他人随意修改...
- div 空隙_尖叫到建筑的空隙
- ie 打开html文件 慢,win7系统使用ie浏览器访问网页显示缓慢、卡死的解决方法
- 【前端实战项目】手把手教你做出小米商城官网(HTML+CSS)
- 【开始报名】第二届中国移动“梧桐杯”大数据应用创新大赛邀你夺52w大奖
- 如何清除windowsoffice KMS激活
- DNS是什么意思有什么作用了
- 关于数据挖掘的something
- Day03-《Guide to Maritime Informatics》-Part I Maritime Data 1-1.2
- 华为手机升级回退_华为荣耀手机系统回退
- LAN 局域网通讯软件 中期报告
- No resource found that matches the given name ‘android.TextAppearance.Materia...
热门文章
- angular5+模块懒加载
- html5 游戏 黑屏,《战地5》游戏黑屏无限加载怎么解决 黑屏解决办法
- Mac下QQ表情管理
- html文件被当毒杀掉了,文件被AVAST误杀怎么办
- 网络语言为你打c,2018十大网络流行语横空出世,你知道几个?你out了吗?
- 论文阅读:Automatic Cobb Angle Detection using Vertebra Detector and Vertebra Corners Regression
- css遇到的坑(一)
- EHS — 学习笔记的开篇问题
- easy connect电脑版_mysql 5.7解压版安装教程
- IOS6 IOS7 Mapkit draw Rout(地图划线)