Android 未进行依赖管理导致的问题:IDE 报错 "Unresolved reference: registerForActivityResult"但是能正常编译运行

前言

谷歌前段时间废弃了 startActicityForResult 取而代之的是 ActicityResultApi ,作为一个紧跟谷歌步伐走的菜鸟开发者,当然想着的是第一时间学习并替换项目中的相关代码。

但是,奇怪的是,IDE 没有警告 startActicityForResult 已废弃,只是在编译时会警告该方法已废弃。

虽然不知道为什么,但是我还是自己手动找到了所有使用了 startActicityForResult 的地方,并且更改为 ActicityResultApi

然而,IDE 却爆红了,提示 Unresolved reference: registerForActivityResult

1.jpg

我一开始怀疑是我写错了,但是我尝试着编译运行了一下,却没有出错,也能够正常运行。

排查过程

问题的根本原因

作为一名面向搜索引擎编程的菜鸡,想到的第一件事当然是先谷歌一下看看咯。

在 stackoverflow 上,大多数人指出出现这个问题是因为 AndroidX 的 activity 依赖版本过低,尚不支持该方法,但是我很确信我的依赖版本已经是最新版本了。

接着往下搜索,发现了有人说可以通过强制转换类型来解决:

private val requestPermission = (this as ComponentActivity).registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->

}

显然,这样只是治标不治本。所以继续搜索。

在谷歌的 issuetracker 上找到两条这样的 issue: Superclass resolution fails with 2 versions of the library in the project Bug: unable to find registerForActivityResult on large project despite having the needed dependencies

上面的 issue 大致是说,如果依赖同一个库有不同的版本,那么 IDE 可能会解析到错误的库版本,导致某些新版本中的 API 无法被 IDE 解析到。

总而言之,这是 IDEA 的一个 BUG 被 Android Studio “继承” 了过来。

那么,这个问题就无法解决,只能等官方修复了吗?

非也,既然知道了导致 BUG 的原因,我们当然是要去避免触发这个 BUG 了。

触发问题的诱因

首先查看一下项目依赖:

2.jpg

很显然可以看到,项目确实同时依赖了两个 acticity 库,其中一个为 1.0.0 版本(无 ActicityResultApi),另外一个为 1.5.0 版本(有 ActicityResultApi),而我很确信,我在主模块(app)中依赖的是 1.5.0 版本,但是 IDE 解析时却解析的是 1.0.0 版本。

找到了问题确实存在,下一步则是找出是哪个模块或者哪个第三方库引用了 1.0.0 版本的 acticity。

依次点击 Adnroid Stuidio 的菜单 File - Project Structure - Dependencies 打开依赖树分析窗口。

然后在 All Modules 中 选中 activity:1.0.0 查看依赖路径:

3.jpg

从依赖路径中能明显看到,1.0.0 版本的 activity 是被 imageEdit 模块的 appcompat 依赖引入的。

我们看一下 iamgeEdit 模块的 build.gradle 模块:

dependencies {
	......

    implementation 'androidx.appcompat:appcompat:1.1.0'
    ......
}

确实发现该模块依赖了 appcompat 库。

注:我之所以言之凿凿的说 activity 是被 appcompat 引入的而非如图中所示的第一级依赖明明是 fragment ,是因为 fragment 其实也是被 appcompat 引入,因为这两个库是相互依赖的关系。并且我的依赖中也只写了 appcompat ,并未添加 fragment ,所以才得出如上结论。

解决方法与思考

解决方法

知道了诱发错误的原因后,更改起来就很简单了,只需要把 iamgeEdit 模块的 appcompat 库等级改为最新版本或支持 ActicityResultApi 的适合自己项目的版本即可。

这里我将 appcompat 更新到最新版本后可以看到错误已消失:

4.jpg

思考

虽然问题已经解决了,但是我们不应该止步于见招拆招,而应该有更多的思考:为什么会出现依赖冲突?

由于这个项目最开始是单模块项目,后来越写越大,才逐渐拆分出其他子模块,所以压根没有做依赖管理。

一个健康的项目,应该有一个统一的依赖管理,而不是随心所欲的各个模块胡乱添加依赖。

在此推荐大佬写的文章:

【Gradle7.0】依赖统一管理的全新方式,了解一下~

文章中也有其他依赖管理方式的链接,大伙可以根据自己的项目需求自行选择。