ViewBinding 超详细原理解析,利用 Kotlin 属性委托实现 VBHelper 方便 VB 在多场景下的生成
ViewBinding 原理解析点这里,主要介绍了以下几点:
- VB 集成与一般使用方式,包括:Activity 、Fragment、Adapter、include、merge、ViewStub
- KT 属性代理与泛型实化类型参数
reified
的介绍 - 通过 KT 属性代理创建 VB
- LayoutInflater 原理与参数解析
- XXXBinding 类的绑定过程
- XXXBinding 类的生成过程
属性委托详解点这里, 要介绍了以下几点:
- 辨析委托模式与代理模式
- 接口委托(Delegated interface)
- 属性委托(Delegated properties)
- 映射委托(Map delegation)
- 延迟属性(lazy properties)
- 非空属性(Delegates.notNull)
- 变量值更新后的监听(Delegates.observable)
- 变量值更新前的拦截(Delegates.vetoable)
- ViewBinding+属性委托
- ViewModel+属性委托
- SP+属性委托
借助 lazy 属性委托的优势:
- Kotlin 1.4 做的优化,当某些委托属性不会使用 KProperty。对于他们来说,在 $$delegatedProperties 中生成 KProperty 对象是多余的。Kotlin 1.4 版本将优化此类情况。如果委托的属性运算符是内联的,并且没有使用 KProperty 参数,则不会生成相应的反射对象。
- 参考博客:What to Expect in Kotlin 1.4 and Beyond | Optimized delegated properties
反射和无反射两种使用方式如下:
- 借助 lazy 属性委托 + 反射 VB 的 inflate 方法
private val binding: ActivityMainBinding by vb()
- 借助 lazy 属性委托 + 传递 inflate 方法引用
private val binding: ActivityMainBinding by vb(ActivityMainBinding::inflate)
核心实现代码
class ActivityVBLazy<T : ViewBinding>(
private val activity: ComponentActivity,
private val kClass: KClass<*>,
private val inflateMethodRef: ((LayoutInflater) -> T)?
) : Lazy<T> {
private var cachedBinding: T? = null
override val value: T
get() {
var viewBinding = cachedBinding
if (viewBinding == null) {
viewBinding = if (inflateMethodRef != null) {
//借助 lazy 属性委托 + 传递 inflate 方法引用
inflateMethodRef.invoke(activity.layoutInflater)
} else {
//借助 lazy 属性委托 + 反射绑定类的 inflate 方法
@Suppress("UNCHECKED_CAST")
kClass.java.getMethod(METHOD_INFLATE, LayoutInflater::class.java)
.invoke(null, activity.layoutInflater) as T
}
activity.setContentView(viewBinding.root)
cachedBinding = viewBinding
}
return viewBinding
}
override fun isInitialized() = cachedBinding != null
}
反射和无反射两种使用方式如下:
- 借助 lazy 属性委托 + 反射 VB 的 inflate 方法
private val binding: FragmentMainBinding by vb()
- 借助 lazy 属性委托 + 传递 inflate 方法引用
private val binding: FragmentMainBinding by vb(FragmentMainBinding::inflate)
核心实现代码
class FragmentVNLazy<T>(
private val fragment: Fragment,
private val kClass: KClass<*>,
private val inflateMethodRef: ((LayoutInflater) -> T)?
) : Lazy<T> {
private var cachedBinding: T? = null
private val clearBindingHandler by lazy(LazyThreadSafetyMode.NONE) { Handler(Looper.getMainLooper()) }
init {
observeFragmentDestroy(fragment) { clearBindingHandler.post { cachedBinding = null } }
}
override val value: T
get() {
var viewBinding = cachedBinding
if (viewBinding == null) {
checkBindingFirstInvoke(fragment)
viewBinding = if (inflateMethodRef != null) {
//借助 lazy 属性委托 + 传递 inflate 方法引用
inflateMethodRef.invoke(fragment.layoutInflater)
} else {
//借助 lazy 属性委托 + 反射绑定类的 inflate 方法
@Suppress("UNCHECKED_CAST")
kClass.java.getMethod(METHOD_INFLATE, LayoutInflater::class.java)
.invoke(null, fragment.layoutInflater) as T
}
cachedBinding = viewBinding
}
return viewBinding!!
}
override fun isInitialized() = cachedBinding != null
}
反射和无反射两种使用方式如下:
- 通过自定义属性代理 + 反射绑定类的 inflate 三参数方法
private val binding: MyViewBinding by vb()
- 通过自定义属性代理 + 传递 inflate 三参数方法引用
private val binding: MyViewBinding by vb(MyViewBinding::inflate)
反射和无反射两种使用方式如下:
- 通过自定义属性代理 + 反射绑定类的 inflate 三参数方法
val holder: BindingViewHolder<LayoutItemTextBinding> by vh(parent)
- 通过自定义属性代理 + 传递绑定类的 inflate 三参数方法引用
val holder: BindingViewHolder<LayoutItemTextBinding> by vh(parent, LayoutItemTextBinding::inflate)
//Step 1. Add the JitPack repository to your build file
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
maven { url 'https://jitpack.io' }
...
}
}
//Step 2. Add the dependency
dependencies {
implementation 'com.github.jaydroid1024.VBHelper:vbhelper:0.0.7'
//anyby 包含 vbhelper 之后会陆续将更多Kotlin 属性委托的最佳实践放在这里
implementation 'com.github.jaydroid1024.VBHelper:anyby:0.0.7'
}
//Step 3. Add the ProGuard
# proguard 官网
#https://www.guardsquare.com/manual/configuration/examples
#https://www.guardsquare.com/proguard
#-keepclassmembers 作用只是保证类成员 ( 成员变量 , 成员方法 ) 不被混淆 , 类名还是会被混淆的
-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
#只保留指定的几个方法,挺高代码混淆/压缩率
public static * bind(android.view.View); #目前反射未采用bind的方式,这个可以不保留
public static * inflate(android.view.LayoutInflater);
public static * inflate(android.view.LayoutInflater, android.view.ViewGroup);
public static * inflate(android.view.LayoutInflater, android.view.ViewGroup,boolean);
}
//推荐上面的混淆方式
#keep 所有类成员,类名会被混淆
#-keepclassmembers class * implements androidx.viewbinding.ViewBinding {
# *;
#}
#keep 类名和所有类成员不会被移除和混淆
#-keepclasseswithmembers class * implements androidx.viewbinding.ViewBinding {
# *;
#}
#keep 类名和类成员都不会被移除和混淆
#-keep class * implements androidx.viewbinding.ViewBinding {
# *;
#}
#keep 类名不会被移除和混淆,类成员都被混淆
#-keep class * implements androidx.viewbinding.ViewBinding
//Build artifacts:
com.github.jaydroid1024.VBHelper:vbhelper:0.0.7
com.github.jaydroid1024.VBHelper:anyby:0.0.7
//Files:
com/github/jaydroid1024/VBHelper/vbhelper/0.0.7
com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7-sources.jar
com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7.aar
com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7.module
com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7.pom
com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7.pom.md5
com/github/jaydroid1024/VBHelper/vbhelper/0.0.7/vbhelper-0.0.7.pom.sha1
com/github/jaydroid1024/VBHelper/anyby/0.0.7
com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7-sources.jar
com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7.aar
com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7.module
com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7.pom
com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7.pom.md5
com/github/jaydroid1024/VBHelper/anyby/0.0.7/anyby-0.0.7.pom.sha1
标签 | New: |
Upgrade: |
Fix: |
---|---|---|---|
描述 | 新添加的功能 | 更新的功能 | 修复的功能 |
2021-9-17
Upgrade:
Activity 中获取VB的方式更新为:借助 Lazy 接口实现委托的方式Upgrade:
Fragment 中获取VB的方式更新为:借助 Lazy 接口实现委托的方式- 借助 Lazy 接口实现委托的方式的优势可以避免生成多余的 KProperty 反射类
2021-9-9
New:
支持在 Activity 中创建 VB 绑定类New:
支持在 Fragment 中创建 VB 绑定类New:
支持在 View 中创建 VB 绑定类New:
支持在 Adapter 中创建包含了绑定类的 BindingViewHolder