一、一句话理解内联函数
内联函数(
inline)是一种编译期优化技巧:它把函数调用“展开”成实际代码,避免函数调用的开销,特别适合高阶函数(比如带 lambda 的函数)。
你可以把它想象成:
- 普通函数:打电话叫别人帮你做事(有通话成本)
- 内联函数:直接把别人做事的步骤抄到你自己的笔记本上(省去打电话)
二、为什么需要它?—— 从 lambda 的性能问题说起
在 Android 开发中,我们大量使用 lambda 表达式,比如:
list.filter { it > 5 }
button.setOnClickListener { showToast() }
viewModelScope.launch { fetchData() }
这些 lambda 在底层会被编译成 匿名内部类(Anonymous Class),每次调用都会:
- 创建新对象(增加 GC 压力)
- 有方法调用开销(对高频操作不友好)
👉 内联函数就是为了解决这个问题!
三、怎么用?加个 inline 关键字
// 普通高阶函数
fun myRun(block: () -> Unit) {
block()
}
// 内联版本
inline fun myInlineRun(block: () -> Unit) {
block()
}
区别在哪?
- 调用
myRun { ... }→ 生成一个 lambda 对象 + 调用方法 - 调用
myInlineRun { ... }→ 编译器直接把{ ... }的代码“粘贴”到调用处,没有额外对象!
四、Android 实战例子 📱
场景1:自定义作用域函数(类似 run, with)
你想写一个安全执行数据库操作的函数:
inline fun <T> withDatabase(block: (db: Database) -> T): T {
val db = getDatabase()
try {
return block(db)
} finally {
db.close()
}
}
// 使用
val user = withDatabase { db ->
db.findUser(123)
}
✅ 加 inline 后:
- 不会产生 lambda 对象
block(db)直接变成db.findUser(123)插入到调用处- 性能更好,尤其在循环中!
场景2:封装协程作用域(避免内存泄漏)
inline fun Fragment.launchAndCollectInLifecycle(
crossinline block: suspend CoroutineScope.() -> Unit
) {
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
block()
}
}
}
// 使用(超简洁!)
launchAndCollectInLifecycle {
viewModel.uiState.collect { updateUI(it) }
}
🔍 注意这里用了
crossinline(后面会解释)
五、关键特性 & 规则
✅ 1. 只对高阶函数有用
内联函数的主要收益来自 避免 lambda 对象创建。如果你的函数不接收 lambda,加 inline 意义不大,甚至可能让 APK 变大(代码膨胀)。
✅ 2. 默认不能作为参数传递(non-local return)
inline fun myLoop(action: (Int) -> Unit) {
for (i in 1..3) {
action(i)
}
}
fun test() {
myLoop {
if (it == 2) return // ❌ 这个 return 会直接退出 test()!
}
}
这叫 non-local return(非局部返回)—— lambda 里的
return会跳出外层函数!
如果你不希望这样,用 crossinline:
inline fun myLoop(crossinline action: (Int) -> Unit) { ... }
// 现在 lambda 里不能写 return,必须用 return@myLoop
✅ 3. 不能递归(或需 noinline)
inline fun factorial(n: Int): Int {
return if (n <= 1) 1 else n * factorial(n - 1) // ❌ 编译错误!
}
因为内联是“展开”,递归会导致无限膨胀。
解决办法:把递归部分标记为 noinline,或干脆别内联。
✅ 4. noininline:选择性不内联某个 lambda
inline fun foo(
inlineAction: () -> Unit,
noinline otherAction: () -> Unit // 这个不内联
) {
inlineAction()
otherAction() // 作为普通函数调用
}
适用场景:当某个 lambda 需要被存储(比如赋值给变量)、或作为参数传给非内联函数时。
六、Android 中哪些地方用了内联函数?
Kotlin 标准库和 Android 框架大量使用 inline,比如:
| 函数 | 说明 |
|---|---|
run, let, apply, with, also | 作用域函数,全部内联 |
repeat | 循环工具,内联避免 lambda 开销 |
viewModelScope.launch | 协程启动,内联优化 |
collect(Flow) | 内联,提升流处理性能 |
你每天都在用,只是没注意到!
七、什么时候不该用 inline?
| 情况 | 建议 |
|---|---|
| 函数很大(>10 行) | ❌ 不要用,会导致代码膨胀(APK 变大) |
| 不接收 lambda | ❌ 没收益 |
| 需要反射调用 | ❌ 内联函数在运行时不存在 |
| 递归函数 | ❌ 无法内联 |
✅ 最佳实践:只对小型、高频调用的高阶函数使用 inline
八、总结一句话
内联函数(
inline)是 Kotlin 的编译期魔法,通过“代码复制”消除 lambda 的对象开销,大幅提升高阶函数性能,是 Android 开发中编写高效、简洁工具函数的秘密武器!
但记住:不是所有函数都要内联,滥用会导致 APK 膨胀。
💡 小贴士:如何判断是否该内联?
问自己两个问题:
- 这个函数接收 lambda 吗?
- 它会被频繁调用(比如在循环、UI 渲染中)吗?
如果两个都是 ✅,那很可能适合 inline!

565

被折叠的 条评论
为什么被折叠?



