Android庫(kù)項(xiàng)目中的資源ID沖突的解決方法
1、前言
Android Studio對(duì)模塊化開(kāi)發(fā)提供的一個(gè)很有用的功能就是可以在主項(xiàng)目下新建庫(kù)項(xiàng)目(Module),但是在使用庫(kù)項(xiàng)目時(shí)卻有一個(gè)問(wèn)題就是資源ID沖突,因?yàn)榫幾g時(shí)SDK會(huì)自動(dòng)幫我們處理這個(gè)問(wèn)題,所以一般我們不會(huì)察覺(jué)到,但是在某些情況下,我們需要意識(shí)到這個(gè)問(wèn)題的存在。
比如,在新建的庫(kù)項(xiàng)目中使用如下代碼:
public void onButtonClick(View view) {
switch (view.getId()) {
case R.id.button_1:
break;
case R.id.button_2;
break;
}
}
IDE會(huì)提示:
Resource IDs cannot be used in a switch statement in Android library modules less.
Validates using resource IDs in a switch statement in Android library module. Resource IDs are non final in the library projects since SDK tools r14, means that the library code cannot treat these IDs as constants.
再比如,我們?cè)趲?kù)項(xiàng)目中以如下方式使用ButterKnife,編譯時(shí)就會(huì)報(bào)錯(cuò)。
@OnClick(R.id.button_1)
public void onButtonClick(View view) {
}
2、分析
無(wú)論是 switch 語(yǔ)句還是注解,都有一個(gè)要求就是使用的值必須是常量。在主項(xiàng)目中, R類中的成員變量都被 static final 修飾,而在庫(kù)項(xiàng)目中僅被 static 修飾。
// 庫(kù)項(xiàng)目中生成的R類:
public final class R {
public static final class id {
public static int button_1 = 0x7f0c0001;
}
}
// 主項(xiàng)目中生成的R類:
public final class R {
public static final class id {
public static final int text_1 = 2131165184;
}
}
為什么庫(kù)項(xiàng)目中生成的資源ID沒(méi)有被 final 修飾呢?官方解釋如下:
Non-constant Fields in Case Labels
當(dāng)多個(gè)庫(kù)項(xiàng)目進(jìn)行合并時(shí),不同項(xiàng)目中的資源ID可能會(huì)重復(fù)。在ADT 14之前,無(wú)論是主項(xiàng)目還是庫(kù)項(xiàng)目,資源ID統(tǒng)一被定義為 final 類型的靜態(tài)變量。這樣照成的結(jié)果就是主項(xiàng)目進(jìn)行編譯時(shí)一旦發(fā)現(xiàn)資源ID沖突,庫(kù)項(xiàng)目中對(duì)應(yīng)的資源文件以及引用資源文件的代碼都需要重新編譯。
如果代碼中使用了被 static final 修飾的變量,那這個(gè)變量實(shí)際上就是一個(gè)常量,編譯時(shí)會(huì)直接使用它的值進(jìn)行替換。在編譯時(shí),如果庫(kù)項(xiàng)目與主項(xiàng)目的資源ID發(fā)生了重復(fù),資源被分配了新的ID后庫(kù)項(xiàng)目之前編譯過(guò)的代碼也就失效了。
那么當(dāng)庫(kù)項(xiàng)目R類中的變量?jī)H被 static 修飾后會(huì)起到什么作用呢,我們可以看一下編譯后的字節(jié)碼再反編譯后的樣子。
// 主項(xiàng)目中的Activity:
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 源代碼:setContentView(R.layout.activity_main);
this.setContentView(2131296283);
}
}
// 庫(kù)項(xiàng)目中的Activity:
public LibActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_lib);
}
}
主項(xiàng)目R類中的資源ID被 static final 修飾,編譯時(shí)直接被替換成了對(duì)應(yīng)的常量。庫(kù)項(xiàng)目R類中的資源ID僅被 static 修飾,所以保留了變量。這樣當(dāng)資源ID發(fā)送沖突時(shí),主項(xiàng)目R類不變,修改庫(kù)項(xiàng)目R類中的變量,庫(kù)項(xiàng)目已經(jīng)編譯過(guò)的代碼仍有效。
3、ButterKnife中的R2類
既然庫(kù)項(xiàng)目中的資源ID不可以定義為常量,那如何在庫(kù)文項(xiàng)目使用ButterKnife呢,作者提供了R2類供我使用。
@OnClick({R2.id.button_1, R2.id.button_2})
public void onButtonClick(View view) {
int id = view.getId();
if (id == R.id.button_1) {
// ...
} else if (id == R.id.button_2) {
// ...
}
}
沒(méi)錯(cuò)在注解中使用R2類,但是在代碼里還是需要使用R類,因?yàn)镽類中的ID不是常量,所以只能使用 if 語(yǔ)句進(jìn)行判斷。
先來(lái)看一下ButterKnife為我們生成的R2類與R類有什么不同:
// 庫(kù)項(xiàng)目中的R類:
public final class R {
public static final class id {
public static int button_1 = 0x7f0c0001;
}
}
// 庫(kù)項(xiàng)目中ButterKnife為我們生成的R2類:
public final class R2 {
public static final class id {
public static final int button_1 = 0x7f0c0001;
}
}
ButterKnife做的工作很簡(jiǎn)單,僅僅是把R類中的變量搬到了R2類里,然后給所有的變量都加上了 final 。根據(jù)前面所說(shuō),當(dāng)項(xiàng)目整體編譯時(shí),庫(kù)項(xiàng)目的資源ID一旦與主項(xiàng)目的資源ID發(fā)送沖突,庫(kù)項(xiàng)目的資源會(huì)被重新分配ID導(dǎo)致其R類被修改。顯然這個(gè)過(guò)程并不涉及R2類,R2類中保留的仍然是過(guò)時(shí)的ID。但是ButterKnife提供的注解的作用是什么,它們并不是為了提供運(yùn)行時(shí)信息,而是為了在編譯時(shí)生成代碼。
public class LibActivity_ViewBinding implements Unbinder {
private LibActivity target;
private View view_button_1;
private View view_button_2;
@UiThread
public LibActivity_ViewBinding(final LibActivity target, View source) {
this.target = target;
View view = Utils.findRequiredView(source, R.id.button_1, "method 'onButtonClick'");
this.view_button_1 = view;
//view.setOnClickListener....
view = Utils.findRequiredView(source, R.id.button_2, "method 'onButtonClick'");
this.view_button_2 = view;
//view.setOnClickListener....
}
}
在ButterKnife生成的代碼中,使用的仍然是R類。R2起到的作用僅僅是提供一個(gè)符號(hào)名,只要讓程序知道在生成代碼時(shí)對(duì)應(yīng)哪一個(gè)變量即可。這個(gè)方法可以說(shuō)是很“tricky”了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
簡(jiǎn)單實(shí)現(xiàn)Android文件上傳
這篇文章主要為大家詳細(xì)介紹了如何簡(jiǎn)單實(shí)現(xiàn)Android文件上傳的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
Android基于TextView實(shí)現(xiàn)的跑馬燈效果實(shí)例
這篇文章主要介紹了Android基于TextView實(shí)現(xiàn)的跑馬燈效果,以完整實(shí)例形式分析了Android使用TextView通過(guò)屬性設(shè)置及功能代碼實(shí)現(xiàn)跑馬燈效果的相關(guān)技巧,需要的朋友可以參考下2016-02-02
解決android studio 打開(kāi)java文件 內(nèi)容全變了的問(wèn)題
這篇文章主要介紹了解決android studio 打開(kāi)java文件 內(nèi)容全變了的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android獲取內(nèi)置sdcard跟外置sdcard路徑
這篇文章主要介紹了Android獲取內(nèi)置sdcard跟外置sdcard路徑的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-09-09
Android實(shí)現(xiàn)字母導(dǎo)航控件的示例代碼
這篇文章主要介紹了通過(guò)自定義View實(shí)現(xiàn)字母導(dǎo)航控件的示例代碼,文中的實(shí)現(xiàn)過(guò)程講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,感興趣的可以學(xué)習(xí)一下2022-01-01
Android UI設(shè)計(jì)與開(kāi)發(fā)之ViewPager仿微信引導(dǎo)界面以及動(dòng)畫(huà)效果
這篇文章主要為大家詳細(xì)介紹了Android UI設(shè)計(jì)與開(kāi)發(fā)之ViewPager仿微信引導(dǎo)界面以及動(dòng)畫(huà)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Android利用RecyclerView實(shí)現(xiàn)列表倒計(jì)時(shí)效果
這篇文章主要為大家詳細(xì)介紹了Android利用RecyclerView實(shí)現(xiàn)列表倒計(jì)時(shí)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09
Android實(shí)現(xiàn)從activity中停止Service的方法
這篇文章主要介紹了Android實(shí)現(xiàn)從activity中停止Service的方法,結(jié)合實(shí)例形式簡(jiǎn)單分析了Android中Service的注冊(cè)、創(chuàng)建及使用stopService停止Service的方法,需要的朋友可以參考下2016-01-01

