将含按钮ID的字符串转Int型ID设置文本时运行崩溃的原因排查
动态获取资源ID导致空指针的原因解析
核心问题
通过getIdentifier()拿到的id变量是无效值,根本无法匹配到布局中的按钮控件。resources.getIdentifier()的API匹配规则要求:
- 第一个参数需要传入资源的简单名称,也就是布局里定义
@+id/testBtn时用到的纯名称testBtn - 第二个参数标注资源类型,这里填
"id"是符合要求的 - 第三个参数传入应用包名即可
你在调用时给第一个参数传了完整字符串"R.id.testBtn",系统会尝试在id类资源列表中查找名为R.id.testBtn的条目,完全找不到对应资源。按照API设计,查找无匹配结果时getIdentifier()会固定返回0,这是一个完全无效的资源ID。
完整崩溃链路
- 执行完
getIdentifier代码后,实际拿到的id变量值为0,不是预期的有效按钮ID - 调用
findViewById<Button>(0)时,系统在当前视图层级中找不到ID为0的控件,返回null - 直接对返回的空值执行
text赋值操作,直接触发testBtn must not be null的空指针崩溃
你直接传入R.id.testBtn作为findViewById参数可以正常运行,是因为这个值是编译期生成的合法资源ID,自然能匹配到对应的按钮实例。
正确实现方式
如果确实需要通过字符串动态获取控件ID,需要保证传给getIdentifier的资源名称是简单名:
- 提前调整存储控件ID的字符串变量,直接存控件的纯名称,而不是完整的R引用串:
// 不要存 "R.id.testBtn",存纯控件名 val stringContainingId = "testBtn" val btnId = resources.getIdentifier(stringContainingId, "id", packageName) - 如果业务上必须保留存储
"R.id.testBtn"格式的字符串,可以通过字符串截取提取最后一段的纯控件名再传入:val stringContainingId = "R.id.testBtn" // 截取最后一段作为资源名 val simpleResourceName = stringContainingId.substringAfterLast(".") val btnId = resources.getIdentifier(simpleResourceName, "id", packageName)
注意事项
- 动态通过字符串查询资源ID的性能低于直接使用编译期生成的R类ID,且如果开启了代码混淆,资源名可能被修改导致查找失败,非必要场景不推荐使用
- 拿到动态查询的ID后,建议先判断其值为非0的有效状态,再执行
findViewById操作,从根源避免空指针
内容的提问来源于stack exchange,提问作者Amy
相关产品推荐
相关产品推荐

