[英]Android Robolectric Library class support. How to have library class R references load from application project
我正在嘗試在Robolectric(android單元測試框架)中實現對Library項目的支持。 我有框架加載庫項目的所有資源,並已測試此工作正常。 這個過程非常簡單,我在project.properties中讀取的RobolectricConfig中查找了循環中的android.library.reference.x值,並逐步遍歷每個項目。
棘手的部分與在運行時從Library Project代碼庫中解析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值不同(app中c = 0x7f050003,庫中c = 0x7f040003)。 我知道庫不知道應用程序,因此它自己的R類不可能生成相同的值,因此應用程序類必須重新映射其值。 我想知道的是,我怎么可能在運行時使用應用程序項目中定義的com.example.lib.R值而不是庫的com.example.lib.R類中定義的值?
失敗的是,當Robolectric Test運行器運行我的測試時,當它進入庫項目代碼庫時,我會查找字符串c ,如下所示:
resources.getString(R.string.c);
所以當我進行查找時得到的是0x7f040003而不是0x7f050003。 似乎大多數值都精確地映射了0x10000,但這並不總是有效,所以我不能依賴它。
我不明白的是,如果我們有兩個具有相同包名的類,並且應用程序中的一個類首先出現在類路徑中(我在運行時通過打印出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/在運行時修改類文件,在適當的地方替換值! 這將是驚人的,但可能相當困難。
Android庫項目本身不運行,它總是通過引用依賴應用程序中的庫並構建該應用程序來間接編譯。
在庫項目的gen文件夾中生成的R.java的唯一目的是完成庫項目本身,這樣IDE在看到像resources.getString(R.string.c);
這樣的代碼時不會彈出任何編譯錯誤resources.getString(R.string.c);
在圖書館項目中。 庫項目的R.java更像是一個臨時文件,它永遠不會被推入應用程序項目的apk文件中。
將庫項目視為兩個部分,純Java源(my-lib / src /)和Android資源(my-lib / res /)。 當您在應用程序項目中引用庫項目時,純Java源代碼將被編譯並最終作為my-lib / bin / my-lib.jar(請注意,在這個所謂的臨時jar中沒有R.class文件) ,它確實將my-lib.jar添加為應用程序項目中的依賴項(在Eclipse Package Explorer中,查看my-app -> Android Dependencies -> my-lib.jar
)。 當ADT認為是時候這樣做時,Android資源是全局合並和編譯的,通常是在為運行/調試構建應用程序項目時。
編譯並打包到最終apk中的R java文件是應用程序項目的gen文件夾下的那些文件,在apk文件中,它們被注入相應的包下,如下所示:
com/
example/
lib/
R <- com.example.lib.R.java
... ... <- package/class from my-lib.jar
app/
R <- com.example.app.R.java
... ... <- package/class from application project
在庫和應用程序項目之間生成的不一致R值不是問題,這由SDK設計保證。 對於你Robolectric拉請求,總是相應地使用來自應用程序項目的R文件。
我提到的大部分信息都可以在官方開發指南的“圖書館項目和開發考慮”部分找到。
由於Library項目的資源始終包含在Application項目中,因此您始終可以將固定值分配給Library項目中的資源。
這可以解決你的問題。 您可以聲明public.xml以指定固定值,以便將相同的值導入到Application項目中。
根據您的資源庫中的資源數量,這可能是乏味的。
您需要在res / values中創建public.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<public type="string" name="c" id="0x7f050003" />
</resources>
有關詳細信息,請查看此帖子
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.