一个上线推广后才发现的大范围崩溃问题

先看问题

下面代码是一个简单的自定义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 {
        	// 一堆处理逻辑
        }
    }
}

代码就是这么个代码,这个代码有问题吗?

答案无非是三种:

  1. 代码100%没问题:那么问题就大了,我们的应用在上线前也是经过好几轮测试的,也都没有测出什么问题,即使线上80%的奔溃率后复测,也没复现。也不能怪测试,因为我们测试机确实有限,都是比较新的机器,这个问题在 Android11 以下才会有。
  2. 代码100%有问题:那么问题在哪里呢?但从上面的代码来看,也不一定有问题哦。
  3. 无法判断有没有问题:这就对了,因为后来我才发现,这个use方法就不止一个,还要看上面代码import了哪个use方法。

这个问题坑在哪里呢?

  1. 自定义View是一个非常常用的操作,这种写法也是很常用的,但是IDE不会报错,编译也不会报错。
  2. 这是在Android11以下才会报错,如果刚好你手上的测试机是Android12以上,那么你在开发阶段发现不了这个问题。
  3. 如果刚好测试们也没有用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 {
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值