我正在尝试在 Robolectric(android 单元测试框架)中实现对库项目的支持。我有框架为库项目加载所有资源,并且已经测试过它可以正常工作。这个过程非常简单,从我在 project.properties 中读取的 RobolectricConfig 并在循环中查找 android.library.reference.x 值,并在每个项目中递归。
棘手的部分与在运行时从库项目代码库中解析 R 引用有关。例如,我们有一个带有 1 个库项目的应用程序,如下所示:
com.example.app依赖于库com.example.lib
这两个项目都有资源。在 com.example.app 项目下,我们有:
gen/com.example.app.R
gen/com.example.lib.R
com.example.app.R 是 com.example.lib.R 的超集,因为它包含 com.example.lib.R 的所有定义,然后是由于其自身资源而添加的定义。我原本以为它们是相同的,但我错了,它们实际上是不同的。然而,对于 R 的内部类((R.string、R.color、R.attr 等),名称/值映射与应用程序项目中 com.example.app.R 中对应的值相同。库项目的 R 类尽管值与应用程序项目中的值不同。
作为简化,假设它们看起来像这样:
package com.example.app;
public final class R {
public static final class string {
public static final int a = 0x7f050001;
public static final int a = 0x7f050002;
public static final int c = 0x7f050003;
}
}
//this file is in the application project
package com.example.lib;
public final class R {
public static final class string {
public static final int c = 0x7f050003;
}
}
然后,在库项目下有gen/com.example.lib.R:
//this file is in the library project
package com.example.lib;
public final class R {
public static final class string {
public static final int c = 0x7f040003;
}
}
所以发生的事情是库在 string.xml 中定义了c而项目没有。库值中 R.string.c 的值与应用程序的 R 值不同(应用程序中的 c = 0x7f050003 和库中的 c = 0x7f040003)。我知道库不了解应用程序,因此它自己的 R 类不可能生成相同的值,因此应用程序类必须重新映射其值。我想知道的是,我怎么可能在运行时使用应用程序项目中定义的 com.example.lib.R 值,而不是库的 com.example.lib.R 类中定义的值?
失败的是,当 Robolectric 测试运行器运行我的测试时,当它进入库项目代码库时,我会查找字符串c,如下所示:
resources.getString(R.string.c);
所以当我进行查找时得到的是 0x7f040003 而不是 0x7f050003。似乎大多数值都恰好比 0x10000 高,但这并不总是有效,所以我不能依赖它。
我不明白的是,如果我们有 2 个具有相同包名的类并且来自应用程序的类在类路径中首先出现(我在运行时通过打印出 System.getProperty("java.class.path" ) 在运行时),为什么库项目仍然使用自己的com.example.lib.R定义而不是应用程序项目中的等效版本?它们都具有完全相同的规范名称。
我想类加载器中的某些内容说这个类(让我们调用它的 MyLibraryActivity)只知道它所依赖的 com.example.lib.R 类,并加载那个类。也许是安全经理或其他人做出这个决定?我真的不知道。但我希望我能以某种方式改变这种行为,以便库项目中的库项目资源查找可以解析为应用程序项目中的版本(毕竟,它位于类路径中)。也许有一种方法可以让我提前强制加载这个类,这样系统就不会尝试重新加载它?
我知道库项目和应用程序项目之间没有依赖关系,我绝对不想添加这种依赖关系,但是我希望运行时的值来自应用程序的 R 类而不是库 R 类.
任何人都知道为什么会发生这种情况,以及我是否有办法强制应用程序 R 类成为应用程序项目引用的类?
- 更新 -
经过一番思考,我认为问题在于 java 编译器内联了这些值,因为它们是最终的静态变量,因此无法解决类加载更改的问题。
我曾经的另一个想法是加载所有库项目的 R 类,并将变量映射到 R 类中的应用程序项目变量。每当调用 getValue 时,我都会以某种方式(在运行时)分析堆栈以确定调用者是谁以及哪个 R 类与该调用者相关联。从那里我可以确定应用程序项目中的关联 ID 是什么,并且查找将按预期工作。
有人知道这是否可能吗?我知道如何在运行时获取堆栈跟踪,并且可以输入一些逻辑来确定调用者是谁,但是确定哪个 R 类与该调用者相关联似乎很复杂、很慢,而且可能容易出错。
-- 再次更新 -- 使用http://www.csg.is.titech.ac.jp/~chiba/javassist/在运行时修改类文件,在适当的地方替换值怎么样!这将是惊人的,但可能非常困难。