問題
問題1:子module里的R.java為何不是常量?
問題2:ButterKnife是怎么解決的照瘾?
問題3:由于ButterKnife的R2.java存在匈棘,導(dǎo)致java compile替換了注解中的常量射富,為何實(shí)際運(yùn)行時(shí)沒出現(xiàn)問題场靴?
解答
問題1 子module里的R.java為何不是常量?
在模塊開發(fā)時(shí)大家都會(huì)發(fā)現(xiàn)module中的R.java是沒有final關(guān)鍵字的虾宇,如:
public static int icon_awesome_arrow=0x7f020188;
public static int icon_awesome_close=0x7f020189;
public static int icon_awesome_large=0x7f02018a;
public static int icon_awesome_small=0x7f02018b;
這樣導(dǎo)致我們使用switch-case也好鹃愤,注解也好簇搅,凡是語法規(guī)定必須要使用常量的地方都無法直接R.drawable了,這是為何呢软吐?
那么我們來假設(shè)如果有final的話會(huì)出現(xiàn)什么問題瘩将,每個(gè)module各自打成aar的時(shí)候AAPT會(huì)單獨(dú)生成R.java,那么在執(zhí)行java compile成class的時(shí)候會(huì)將常量直接替換成具體的值,如:
@BindView(2131755430)
protected FrameLayout errorViewParent;
由于各個(gè)module間無法保證R.java中變量對(duì)應(yīng)著的數(shù)值不同姿现,所以如果出現(xiàn)相同的豈不是尷尬了肠仪?當(dāng)然假如相同也沒關(guān)系,可以反過來取到int數(shù)值后去找對(duì)應(yīng)的resName备典,然后再去app的大R.java里去取异旧,再將各個(gè)module的替換掉。mdzz想著就麻煩提佣,索性不給module的R.java定義為常量了吮蛹。
俗話說的好,天王蓋地虎镐依,小雞燉蘑菇匹涮,沒有什么是他們那些大佬無法解決的,比如ButterKnife就提供了一個(gè)方案槐壳。
問題2:ButterKnife是怎么解決的然低?
mmp,既然官方搞得R.java不是常量务唐,那我們就copy一份R.java雳攘,搞個(gè)R2.java,然后把所有變量都加上final關(guān)鍵字枫笛,然后相關(guān)地方直接引用R2不就得了咯吨灭。沒錯(cuò),就是這么簡單刑巧,讓我們來看看相關(guān)gradle plugin是怎樣做的喧兄。
private fun applyPlugin(variants: DomainObjectSet) {
variants.all { variant ->
variant.outputs.forEach { output ->
val processResources = output.processResources
// TODO proper task registered as source-generating?
processResources.doLast {
val pathToR = processResources.packageForR.replace('.', File.separatorChar)
val rFile = processResources.sourceOutputDir.resolve(pathToR).resolve("R.java")
FinalRClassBuilder.brewJava(rFile, processResources.sourceOutputDir,
processResources.packageForR, "R2")
}
}
}
}
看到?jīng)],與R.java一樣的目錄啊楚,一樣的包名吠冤,然后改成R2,我水土都不服舅服你恭理。mmp again拯辙,突然發(fā)現(xiàn)這個(gè)文件用kotlin寫了?
關(guān)于FinalRClassBuilder怎么生成的文件我就不寫了颜价,代碼都在涯保。
問題3:由于ButterKnife的R2.java存在,導(dǎo)致java compile替換了注解中的常量周伦,為何實(shí)際運(yùn)行時(shí)沒出現(xiàn)問題夕春?
一般這個(gè)問題會(huì)去思考的人比較少,可能大佬們覺得太簡單横辆,那我等菜比就來瞅瞅撇他。module實(shí)際打成aar的時(shí)候是沒有把R.java打進(jìn)來的茄猫,各位可以自行解壓你們aar中的classes.jar,我就不截圖了困肩。大家會(huì)發(fā)現(xiàn)划纽,R.java沒有,但是R2.java有啊锌畸,那么我們之前在module中的注解里使用的R2.drawable勇劣,在javac的時(shí)候直接替換成具體的int值了,那豈不是打包后要蹦蹦蹦啊潭枣。然而比默,并沒有,這是為啥呢盆犁?
我們來看ButterKnife的APT生成的ViewBinding的代碼:
public BazhangActivity_ViewBinding(T target, View source) {
this.target = target;
target.parentLayout = Utils.findRequiredViewAsType(source, R.id.parent_layout, "field 'parentLayout'", ViewGroup.class);
target.contentLayout = Utils.findRequiredViewAsType(source, R.id.content, "field 'contentLayout'", FrameLayout.class);
}
擦命咐,發(fā)現(xiàn)了吧,實(shí)際findViewById取得并不是我們注解里的int值谐岁,而仍然是R.id醋奠,也就是說注解里傳的R.id在編譯時(shí)已經(jīng)把該做的都做完了,APT生成的代碼取得還是AAPT最終為app生成的R.java伊佃,那么也就是最終app打包后BazhangActivity_ViewBinding里的值和原BazhangActivity的值基本都是不同的窜司。假如還在用反射去取注解里的int值來實(shí)現(xiàn)findViewById的話就真的尷尬了。