先看问题
下面代码是一个简单的自定义View,很常用是吧,你们先看下这段代码有没有问题?
class TestView : FrameLayout {
// 前面是一个堆构造方法,最后都会调用这个init()方法
private fun init(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
// 主要是这里用了一个use方法,原意是想自动调用recycle方法释放内存的
context.obtainStyledAttributes(attrs, R.styleable.TestView, defStyleAttr, 0).use {
// 一堆处理逻辑
}
}
}
代码就是这么个代码,这个代码有问题吗?
答案无非是三种:
- 代码100%没问题:那么问题就大了,我们的应用在上线前也是经过好几轮测试的,也都没有测出什么问题,即使线上80%的奔溃率后复测,也没复现。也不能怪测试,因为我们测试机确实有限,都是比较新的机器,这个问题在
Android11以下才会有。 - 代码100%有问题:那么问题在哪里呢?但从上面的代码来看,也不一定有问题哦。
- 无法判断有没有问题:这就对了,因为后来我才发现,这个use方法就不止一个,还要看上面代码import了哪个use方法。
这个问题坑在哪里呢?
- 自定义View是一个非常常用的操作,这种写法也是很常用的,但是IDE不会报错,编译也不会报错。
- 这是在Android11以下才会报错,如果刚好你手上的测试机是Android12以上,那么你在开发阶段发现不了这个问题。
- 如果刚好测试们也没有用Android11以下的机子来测试,那么恭喜你,你会在上线后才发现这个问题。
问题分析
首先 context.obtainStyledAttributes(attrs, R.styleable.InputView2, defStyleAttr, 0) 会返回一个 TypedArray 对象,这个对象最后是要调用 recycle() 方法释放的。
然后我们懒惰用了kotlin提供的 use() 方法,让它自动释放,看看这个use长什么样:
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
// 省略代码
} finally {
// 省略代码
// 反正最后就是调用 close() 方法,也就是 Closeable 的 close() 方法
close()
}
}
从上面代码可以看出最后是调用了 Closeable 的 close() 方法,而 TypedArray 实现了这个接口:
public class TypedArray implements AutoCloseable {
public void close() {
recycle();
}
}
一直到这里都是正常的,所以编译器也不会报错,测试也没有测出来。
那么问题来了,我去找了Android11的 TypedArray 的源码,并没有实现 Closeable 接口:
public class TypedArray {
// 省略省略
}
解决方法
既然是 use() 引起的问题,那么不用它就是啦,直接用原始的 try catch 就行了。
但是,我发现 Jetpack 帮我们写了扩展方法:
public inline fun <R> TypedArray.use(block: (TypedArray) -> R): R {
return block(this).also {
recycle()
}
}
所以,我们直接用就行啦,不过一定要记住 import 对了:
import androidx.core.content.res.use
class TestView : FrameLayout {
private fun init(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
context.obtainStyledAttributes(attrs, R.styleable.TestView, defStyleAttr, 0).use {
}
}
}
本文讨论了一个关于自定义View中使用`use`方法导致的Android11及以下版本内存管理问题,尽管IDE和编译阶段未报错,但在实际运行时可能导致崩溃。解决办法是禁用`use`并直接处理资源释放,或者正确导入Jetpack的`use`扩展方法。
756

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



