概述 (Summary)
最近在項(xiàng)目中,遇到一個(gè)關(guān)于使用ViewBinding的問題
在使用<include />標(biāo)簽且layout的文件來(lái)自aar中的文件時(shí),
生成的ViewBinding中映射layout的類型是View,而不是我們期望的XxxBinding,
但是如果layout文件來(lái)自同一個(gè)工程中的另一個(gè)module,則會(huì)生成XxxBinding類型。
問題 (Issue)
layout文件:
<!-- activity_awesome.xml -->
<androidx.constraintlayout.widget.ConstraintLayout>
<include android:id="@+id/includes" layout="@layout/included_buttons" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- included_buttons.xml 在aar中-->
<androidx.constraintlayout.widget.ConstraintLayout>
<Button android:id="@+id/include_me" />
</androidx.constraintlayout.widget.ConstraintLayout>
生成的Binding文件
public final class ActivityAwesomeBinding implements ViewBinding {
@NonNull
public final View includes;
}
分析 (Analysis)
-
對(duì)比正常生成和非正常生成時(shí), gradle tasks的執(zhí)行情況
當(dāng)layout文件在同工程的另一個(gè)module中,能夠正常生成
XxxBinding類型時(shí),會(huì)多執(zhí)行該module的相關(guān)tasks:
dataBindingMergeDependencyArtifactsDebug
dataBindingMergeGenClassesDebug
dataBindingGenBaseClassesDebug -
分析Android Gradle Plugin源碼
android.databinding.tool.writer.ViewBinderKt#toViewBinder 該方法直接忽略了aar中l(wèi)ayout對(duì)應(yīng)的XxxBinding
// Check to make sure that the ID matches a binding. Ignored tags like <merge> or <fragment>
// might have an ID but not have an actual binding. Only use ID if a match was found.
val rootBinding = bindings.singleOrNull { it.id == id }
if (rootBinding != null) {
return RootNode.Binding(rootBinding)
}
結(jié)論 (Conclude)
目前AGP 7.2.0 處理ViewBinding生成代碼的方式,雖然aar的模塊在生成aar的階段,已經(jīng)生成了XxxBinding.java文件,但是由于沒有運(yùn)行ViewBinding相關(guān)的Gradle Task,導(dǎo)致依賴aar的模塊沒有將aar中的XxxBinding.java加入到后續(xù)配置查詢的binding list中,最終該類型被忽略,默認(rèn)返回了View類型
替代方法 (Workaround)
由于底層插件的限制,我們沒有辦法直接使用XxxBinding (或者說時(shí)間成本高,沒有必要自己手動(dòng)寫一個(gè)生成代碼的插件),但是我們依然可以映射出XxxBinding
// Activity.kt
class Activity {
private val includedButtonsBinding :IncludedButtonsBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val rootBinding = ActivityAwesomeBinding.inflate(inflater, container, false)
includedButtonsBinding = IncludedButtonsBinding.bind(ActivityAwesomeBinding.includes)
return rootBinding.root
}
}