承香墨影
只分享最有用的原创技术干货!
关注
在 Android 开发中,使用 Intent 在不同的页面之间,传递数据,是一个常用的操作。
本文中,利用了 Kotlin 的扩展和委托,让这个操作变得更加易用和简单,思路很有意思,和 Google 前段时间发布的 Kotlin KTX 有点像,利用这样的思路,你也可以扩展出很多系统 Api 不支持的功能。
— 承香墨影
作者 | Limo Saplf
原文 | <使用 Kotlin 优化 Intent 数据传递>
文本遵循 CC BY-NC-SA 3.0 协议,授权转载
这篇文章来源于 You won't believe this one weird trick to handle Android Intent extras with Kotlin ,本来准备翻译一遍,但是担心理解不够深入,索性按着自己的理解整理一遍。十分感谢 @Eugenio Marletti 的思路。
首先,这篇文章可能会涉及到的知识点有:
伴生对象
对象表达式与对象声明
扩展函数
扩展属性
高阶函数( lambda
表达式)
带接收者的函数字面值
委托属性
在 Android 开发中, Activity
之间的数据传递是不可避免的。初次接触 Android 编程的时候,许多教程会写出这样的代码:
// 第一个 Activity
Intent intent = new Intent(context, AimActivity.class);
intent.putExtra("msg", message);
startActivity(intent);
// AimActivity
this.message = getIntent().getStringExtra("msg");
这样写当然没有错,的确达到了传递数据的目的,但是,这却会让代码难以维护。稍微有些开发经验的,都会选择将 Extra
信息的键值抽出变为常量,并且把许多类似的键值放在同一个地方,避免出现键值的冲突。
为了更好的可读性,还有一些人会这样去封装 Intent
:
private static final String MSG_KEY = "key for message";
@Nullable public static String getMessage(@NonNull Intent intent) {
return intent.getStringExtra(MSG_KEY);
}
public static void setMessage(@NonNull Intent intent, String message) {
intent.putExtra(MSG_KEY, message);
}
这样一来,读写 Intent
的确如读写类成员属性一般了,可是,不得不说,这也极大地增加了代码的编写量。当然,或者可以通过注解、动态生成代码的方式来解决这一问题,但这不是本篇文章的解决方案。
在 Java 的实现方式中,最适合阅读、最符合面向对象思想的数据传递方式当属最后一种,那,下面就用一些 Kotlin 的特性来实现:
object IntentOptions {
private const val MSG_KEY = "key for message"
fun Intent.getMessage(): String? = getStringExtra(MSG_KEY)
fun Intent.setMessage(message: String?) {
putExtra(MSG_KEY, message)
}
}
// 数据的存储与获取
with(IntentOptions) {
intent.setMessage("message")
message = intent.getMessage()
}
可以看到,利用 Kotlin 提供的扩展函数的特性,我们就可以在 Intent
对象上直接使用 getter/setter
了,这是 Java 语言做不到的。
需要注意的是 with
表达式的使用,这涉及到 扩展函数 的作用域问题。在 Kotlin 中,扩展函数的作用范围只在其定义范围内。一般而言,我们把许多扩展函数定义在了顶级作用域中,这样在整个 App ,被扩展的对象实例都可以使用该函数。但是考虑到,我们不能在 Intent
对象上无限制地添加 getter/setter
,所以利用 object
,让这些扩展函数只在该对象的作用域内有效。在实际使用中,这个 object
可以是某个 Activity
的伴生对象。
到这里,我们可以更进一步,使用 扩展属性 ,让赋值/取值的过程更符合 Kotlin 的语言规范:
object IntentOptions {
private const val MSG_KEY = "key for message"
var Intent.message: String?
get() = getStringExtra(MSG_KEY)
set(message) {
putExtra(MSG_KEY, message)
}
}
// 使用
with(IntentOptions) {
intent.message = "message"
message = intent.message
}
嗯,不论写那个 object
的过程怎样,至少,在使用这样的 Intent
方面,体验到了读写原生对象的便利啊。
委托?为什么要用委托?上面的代码我们使用委托的模式改写一下:
class IntentExtraStringDelegate(val key: String) {
fun getValue(intent: Intent): String? =
intent.getStringExtra(name)
fun setValue(intent: Intent, value: String?) {
intent.putExtra(name, value)
}
}
object IntentOptions {
private val messageDelegate = IntentExtraStringDelegate("key for message")
var Intent.message: String?
get() = messageDelegate.getValue(this)
set(message) = messageDelegate.setValue(this, message)
}
可以看到,委托就是把 Intent
内容的读写操作抽取出来,以便复用。进行了这样的抽取之后,我们又可以在 Kotlin 的语言特性中找到这样的实现:
class IntentExtraString(private val key: String) {
operator fun getValue(intent: Intent, property: KProperty<*>): String? =
intent.getStringExtra(key)
operator fun setValue(intent: Intent, property: KProperty<*>, value: String?) {
intent.putExtra(key, value)
}
}
// 使用
object IntentOptions {
var Intent.message by IntentExtraString("key for message")
}
啊哈,这样一来,相当于仅仅是对 Intent
内的变量进行了一下声明,就可以在对应的范围内使用了!而存储的键值,也完全可以省略,直接使用字段名:
class IntentExtraString(private val key: String? = null) {
private val KProperty<*>.extraName: String
get() = this@IntentExtraString.key ?: name
operator fun getValue(intent: Intent, property: KProperty<*>): String? =
intent.getStringExtra(property.extraName)
operator fun setValue(intent: Intent, property: KProperty<*>, value: String?) {
intent.putExtra(property.extraName, value)
}
}
一般而言,在 Activity
可以这样使用:
class Activity : AppCompatActivity() {
companion object IntentOptions {
var Intent.id by IntentExtraString()
var Intent.name by IntentExtraString()
var Intent.message by IntentExtraString()
}
fun test(intent: Intent) {
intent.id = "1"
intent.name = "pass"
intent.message = "message"
}
}
fun testOutSide(intent: Intent) = with(Activity.IntentOptions) {
val id = intent.id
val name = intent.name
val message = intent.message
}
以上,就是使用 Kotlin 优化 Intent
数据传输的基本思路。当然,在这个思路下,还有许多可以封装的,比如,自定义类型数据的传输——可以去看一看原作者的开源库 android-extras-delegates 。
今天在公众号后台回复成长『 成长 』,将会得到我整理的一些学习资料,也能回复『 加群 』,一起学习进步。android-extras-delegates:
https://github.com/Takhion/android-extras-delegates
推荐阅读:
漫画:程序员,你能“管理”好你的产品经理吗?
官方新出的 Kotlin 扩展库 KTX
不懂批判性思维,可能正在限制你的程序员生涯
Android 开发,遇上 Emoji 头疼吗?
Andorid 签名和多渠道打包方案 | VasDolly