20、Intent与Bundle KTX:Intent传值与Bundle存取的KTX扩展

说实话,Intent和Bundle这对组合,在Android开发里就像吃饭用的筷子一样频繁。但用久了你会发现,原生API写起来有点啰嗦——每次都要写一堆putExtragetStringExtra,还要处理各种类型转换。KTX扩展库就是来解决这个痛点的。

我个人习惯把Intent和Bundle的KTX扩展分成两类:一类是简化传参的,另一类是优化存取的。咱们一个一个来看。

20.1 Intent KTX:告别繁琐的putExtra

先看个对比。原生写法是这样的:

// 原生写法
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("user_id", 123)
intent.putExtra("user_name", "张三")
intent.putExtra("is_vip", true)
startActivity(intent)

用KTX之后呢?

// KTX写法
startActivity<DetailActivity>(
    "user_id" to 123,
    "user_name" to "张三",
    "is_vip" to true
)

是不是清爽多了?startActivity<T>这个扩展函数,直接接收可变数量的Pair参数,内部帮你组装好Intent。我在项目里用了这个之后,再也不想写回原生方式了。

核心扩展函数一览

  • intentOf(vararg params: Pair<String, Any?>):快速创建Intent
  • startActivity<T>(vararg params: Pair<String, Any?>):创建并启动Activity
  • intentFor<T>(vararg params: Pair<String, Any?>):创建Intent的另一种形式

20.2 Bundle KTX:存取数据更优雅

Bundle的KTX扩展,说白了就是让你用更Kotlin的方式操作键值对。原生写法里,你经常要写:

// 原生写法
val bundle = Bundle()
bundle.putString("name", "李四")
bundle.putInt("age", 25)
bundle.putBoolean("active", true)

KTX版本呢?

// KTX写法
val bundle = bundleOf(
    "name" to "李四",
    "age" to 25,
    "active" to true
)

嗯,这里要注意:bundleOf返回的是Bundle类型,但内部做了类型推断。你传Int就是Int,传String就是String,不用手动指定类型。

小技巧bundleOf支持最多10个键值对。如果你需要更多,建议考虑用其他数据结构,或者分多次创建再合并。

20.3 读取数据:用扩展属性更安全

读取数据这块,KTX提供了类型安全的扩展属性。举个例子:

// 原生写法(容易出错)
val name = intent.getStringExtra("name") ?: ""
val age = intent.getIntExtra("age", 0)

// KTX写法(类型安全)
val name: String by intent.extras!!.string("name")
val age: Int by intent.extras!!.int("age")

为什么说类型安全?因为KTX的扩展属性会做类型检查。我曾经在项目里遇到过一个问题:某个字段本来是Int,但传值的时候不小心传成了String,原生API不会报错,只是返回默认值。用KTX的扩展属性,编译期就能发现类型不匹配。

避坑指南:使用by intent.extras!!这种委托方式时,一定要确保extras不为null。我建议在onCreate里先做判空处理:

val extras = intent.extras ?: return@onCreate

20.4 实战:封装一个通用的传参工具

结合KTX扩展,我们可以封装一个更易用的传参工具。我在项目里是这样做的:

// 定义参数键
object Args {
    const val USER_ID = "user_id"
    const val USER_NAME = "user_name"
    const val USER_AGE = "user_age"
}

// 使用KTX扩展
class DetailActivity : AppCompatActivity() {
    private val userId: Int by lazy {
        intent.extras?.int(Args.USER_ID) ?: -1
    }
    
    private val userName: String by lazy {
        intent.extras?.string(Args.USER_NAME) ?: ""
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 直接使用userId和userName
    }
}

// 启动时
startActivity<DetailActivity>(
    Args.USER_ID to 123,
    Args.USER_NAME to "王五"
)

这样写的好处是:参数定义集中管理,启动和读取都用了KTX扩展,代码量减少了一半以上。

20.5 知识体系图

下面这张图展示了Intent与Bundle KTX的核心知识结构:

Intent与Bundle KTX 知识体系 Intent & Bundle KTX Intent 扩展 startActivity<T> intentOf / intentFor Bundle 扩展 bundleOf 扩展属性读取 类型安全 · 代码简洁 · 减少样板代码

20.6 性能与注意事项

场景 原生API KTX扩展 我的建议
简单传参(1-3个) 啰嗦 简洁 用KTX
复杂对象传参 需序列化 需序列化 两者一样,推荐Parcelable
大量数据(>10个) 可读性差 可读性一般 考虑用ViewModel或单例
跨进程通信 支持 支持 注意Binder缓冲区限制

重要提醒:KTX扩展只是语法糖,底层还是调用了原生API。所以性能上几乎没有差别。但要注意:

  • 不要传太大的数据(超过1MB),否则会触发TransactionTooLargeException
  • 自定义对象必须实现Parcelable或Serializable
  • KTX扩展不支持LiveData、Flow等响应式数据直接传值

最后说一句:KTX扩展不是银弹,但它确实让日常开发舒服了很多。你想想看,每次少写几行代码,一天下来能省多少时间?我个人是把这些扩展函数用到了极致,连Fragment的arguments都用bundleOf来初始化。嗯,这就是Kotlin的魅力所在——让代码更简洁,让开发者更专注。

公众号:蓝海资料掘金营,微信deep3321