Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

利用 Kotlin 属性委托实现 VBHelper 方便 ViewBinding 在多场景下的生成

Notifications You must be signed in to change notification settings

jaydroid1024/anyby

Repository files navigation

VBHelper

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

VBHelper 功能与使用

1. 在 Activity 中创建 ViewBinding 绑定类

反射和无反射两种使用方式如下:

  1. 借助 lazy 属性委托 + 反射 VB 的 inflate 方法
private val binding: ActivityMainBinding by vb()
  1. 借助 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
}

2. 在 Fragment 中创建 ViewBinding 绑定类

反射和无反射两种使用方式如下:

  1. 借助 lazy 属性委托 + 反射 VB 的 inflate 方法
private val binding: FragmentMainBinding by vb()
  1. 借助 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
}

3. 在 View 中创建 ViewBinding 绑定类

反射和无反射两种使用方式如下:

  1. 通过自定义属性代理 + 反射绑定类的 inflate 三参数方法
private val binding: MyViewBinding by vb()
  1. 通过自定义属性代理 + 传递 inflate 三参数方法引用
private val binding: MyViewBinding by vb(MyViewBinding::inflate)

4. 在 Adapter 中创建包含了绑定类的 BindingViewHolder

反射和无反射两种使用方式如下:

  1. 通过自定义属性代理 + 反射绑定类的 inflate 三参数方法
val holder: BindingViewHolder<LayoutItemTextBinding> by vh(parent)
  1. 通过自定义属性代理 + 传递绑定类的 inflate 三参数方法引用
val holder: BindingViewHolder<LayoutItemTextBinding> by vh(parent, LayoutItemTextBinding::inflate)

VBHelper 集成

//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:
描述 新添加的功能 更新的功能 修复的功能

V1.0.2

2021年9月17日

  • Upgrade: Activity 中获取VB的方式更新为:借助 Lazy 接口实现委托的方式
  • Upgrade: Fragment 中获取VB的方式更新为:借助 Lazy 接口实现委托的方式
  • 借助 Lazy 接口实现委托的方式的优势可以避免生成多余的 KProperty 反射类

V1.0.1

2021年9月9日

  • New: 支持在 Activity 中创建 VB 绑定类
  • New: 支持在 Fragment 中创建 VB 绑定类
  • New: 支持在 View 中创建 VB 绑定类
  • New: 支持在 Adapter 中创建包含了绑定类的 BindingViewHolder

About

利用 Kotlin 属性委托实现 VBHelper 方便 ViewBinding 在多场景下的生成

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

AltStyle によって変換されたページ (->オリジナル) /