最近我们在做区块链相关的钱包项目, 新的 App 使用全新的技术栈. 在 Android 中我们使用 Kotlin+RxJava+Android Architecture Components, 在 iOS 中使用 Swift+RxSwift. 本文不讨论 App 的架构, 只讨论项目中所使用到的 Kotlin 的特性.
星战小兵. jpg
背景
最近我们在做区块链相关的钱包项目, 新的 App 使用全新的技术栈. 在 Android 中我们使用 Kotlin+RxJava+Android Architecture Components, 在 iOS 中使用 Swift+RxSwift. 本文不讨论 App 的架构, 只讨论项目中所使用到的 Kotlin 的特性.
在 Android 的 App 中, 可以毫不夸张地说, 我们 95% 以上的代码使用了 Kotlin 开发的. 由此, 很有必要对这一阶段使用 Kotlin 做一个简单的小结.
使用的 Kotlin 特性:
一. 扩展函数
Kotlin 允许开发者在不改变已有类的情况下, 为某个类添加新的函数. 这个特性叫做扩展函数.
举一个简单的例子. 如果要关闭一个 I/O 流, 使用 Java 可能是写一个工具方法.
- /**
- * 安全关闭 io 流
- * @param closeable
- */
- public static void closeQuietly(Closeable closeable) {
- if (closeable != null) {
- try {
- closeable.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
对 Kotlin 而言, 可以对 Closeable 扩展一个函数 closeQuietly().
- fun Closeable?.closeQuietly() {
- try {
- this?.close()
- } catch (e: Throwable) {
- }
- }
之后, 任何实现了 Closeable 接口的类, 都可以使用它本身的 closeQuietly()方法来关闭流. 我们不再需要那个工具方法了.
在项目中, 我们使用扩展函数对 Glide 做了封装, 大大简化了 Glide 的使用.
- /**
- * 占位符矩形
- */
- fun ImageView.load(url: String) {
- get(url).placeholder(R.drawable.shape_default_rec_bg)
- .error(R.drawable.shape_default_rec_bg)
- .into(this)
- }
- /**
- * 占位符圆角矩形
- */
- fun ImageView.loadRound(url: String) {
- get(url).placeholder(R.drawable.shape_default_round_bg)
- .error(R.drawable.shape_default_round_bg)
- // .apply(RequestOptions.bitmapTransform(RoundedCornersTransformation(DisplayUtil.dp2px(context, 6f), 0)))
- .transform(RoundedCornersTransformation(DisplayUtil.dp2px(context, 6f), 0))
- .into(this)
- }
- /**
- * 占位符圆形
- */
- fun ImageView.loadCircle(url: Drawable) {
- get(url).placeholder(R.drawable.shape_default_circle_bg)
- .error(R.drawable.shape_default_circle_bg)
- .into(this)
- }
- fun ImageView.loadCircle(url: String) {
- get(url).placeholder(R.drawable.shape_default_circle_bg)
- .error(R.drawable.shape_default_circle_bg)
- .into(this)
- }
- fun ImageView.get(url: String): GlideRequest = GlideApp.with(context).load(url)
- fun ImageView.get(url: Drawable): GlideRequest = GlideApp.with(context).load(url)
除此之外, 我们还很多地方都用到了扩展函数.
我顺便更新了我的 Kolin 的工具类库, 它包括各种 utils 和各种 extension
https://github.com/fengzhizi715/SAF-Kotlin-Utils
二. 尾随闭包
一开始我并不了解这个概念. 偶然间我看到我们的小伙伴在使用 RxBus 时, 写下了这样的代码:
RxBus.get().register(LogoutEvent::class.java) { refresh() }
当时我感觉很疑惑, 因为 RxBus 是我写的, 记得没有提供这样的方法啊. 点击 register()方法进去看之后, 发现 register 是这样的:
- public Disposable register(Class eventType, Consumer onNext) {
- return toObservable(eventType).observeOn(AndroidSchedulers.mainThread()).subscribe(onNext);
- }
由于使用了 Kotlin, 该 register 方法的使用可以简化成这样:
- RxBus.get().register(LogoutEvent::class.java,{
- refresh()
- })
由于 register()最后一个参数是一个方法或者说是一个闭包, 可以把方法或者闭包提到最外面. 变成项目中看到的样子:
RxBus.get().register(LogoutEvent::class.java) { refresh() }
这就是尾随闭包, 可以让代码看起来更加简洁.
三. with 的用法
with 是将某个对象作为函数的参数, 在函数块内可以通过 this 指代该对象. 在函数块内可以直接调用对象的方法或者属性.
- /**
- * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
- */
- @kotlin.internal.InlineOnly
- public inline fun with(receiver: T, block: T.() -> R): R {
- contract {
- callsInPlace(block, InvocationKind.EXACTLY_ONCE)
- }
- return receiver.block()
- }
在使用 with 之前的某个 Adapter
- class AppPublisherAdapter : BaseAdapter() {
- override fun getLayoutId(viewType: Int): Int = R.layout.cell_app_publisher
- override fun onBindViewHolderImpl(holder: BaseViewHolder, position: Int,content: BoundAppInfoResponse.AppInfo) {
- holder.itemView.tv_game_name.text = content.name
- if (content.is_bound) {
- holder.itemView.tv_bound_user_name.text = content.bound_user_name
- holder.itemView.tv_bound_user_name.setTextColor(context.resources.getColor(R.color.color_bound_user_name))
- } else {
- holder.itemView.tv_bound_user_name.text = context.getString(R.string.bind_on_account)
- holder.itemView.tv_bound_user_name.setTextColor(context.resources.getColor(R.color.color_bind_on_account))
- }
- holder.itemView.iv_game_icon.load(content.logo_url)
- }
- }
使用 with 之后, 该函数块可以省略 "content."
- class AppPublisherAdapter : BaseAdapter() {
- override fun getLayoutId(viewType: Int): Int = R.layout.cell_app_publisher
- override fun onBindViewHolderImpl(holder: BaseViewHolder, position: Int, content: BoundAppInfoResponse.AppInfo) {
- with(content) {
- holder.itemView.tv_game_name.text = name
- if (is_bound) {
- holder.itemView.tv_bound_user_name.text = bound_user_name
- holder.itemView.tv_bound_user_name.setTextColor(context.color(R.color.color_bound_user_name))
- } else {
- holder.itemView.tv_bound_user_name.text = context.string(R.string.bind_on_account)
- holder.itemView.tv_bound_user_name.setTextColor(context.color(R.color.color_bind_on_account))
- }
- holder.itemView.iv_game_icon.load(logo_url)
- }
- }
- }
四. 其他
这部分的内容并不是 Kotlin 的特性, 是我使用 Kotlin 开发的工具. 比如日志框架 L 以及 Retrofit 的日志拦截器. 这些库, 其实很早就开发了, 最近稍微升级了一下功能.
L 的 github 地址:
https://github.com/fengzhizi715/SAF-Kotlin-log
Retrofit 日志拦截器的 github 地址:
https://github.com/fengzhizi715/saf-logginginterceptor
日志拦截器的效果图:
request 的效果图. jpeg
response 的效果图. jpeg
总结
Kotlin 吸收了多种语言的优点, 相对于 Java 有很多激动人心的特性, 极大地提高了开发效率. 本文介绍的特性也只是沧海一粟. 接下来, 我会整理更多项目中所使用的 Kotlin 特性.
BTW, 我在写这篇文章的时候国内第一个钱包版本刚刚做完, 开始第一轮测试.
来源: http://mobile.51cto.com/hot-571389.htm