前言
公众号:ByteCode,致力于分享最新技术原创文章,涉及 Kotlin、Jetpack、译文、系统源码、 LeetCode / 剑指 Offer / 多线程 / 国内外大厂算法题 等等一系列文章。
最近小伙们应该都会收到 Kotlin 1.4.20
的升级通知,在 Kotlin 1.4.20
中做了一个重要的更新 如下图所示:
简单总结一下,主要有以下几点:
- 废弃了
kotlin-android-extensions
编译插件 Parcelable
相关的功能,移到了新的插件kotlin-parcelize
按照 Google 的解释,
kotlin-android-extensions
插件只会保留至少一年的时间,将会在 2021 年 9 月或之后的 Kotlin 版本中将被移除
kotlin-android-extensions
主要有以下两个功能:
- 使用 Kotlin 合成方法(Synthetic 视图)取代
findViewById
,通过引入kotlinx.android.synthetic
可以直接使用控件的 ID,我猜当初也是因为这个特性,吸引了很多开发者开始学习和尝试使用 Kotlin - 手动实现
Parcelize
比较麻烦,所以 Kotlin 提供了@Parcelize
注解帮助快速实现 Parcelize
其实这并不是什么新的新闻了,早在 2019 年的时候,Google 就提出了不建议在项目中使用 kotlinx.android.synthetic
,详见这个 commit,部分内容如下图所示:
通过引入 kotlinx.android.synthetic
可以直接使用控件的 ID,这么方便为什么不建议使用?主要有以下问题:
- 通过 Kotlin 合成方法(Synthetic 视图)取代
findViewById
,这是通过全局空间缓存 ID,与 Layout 无关,没有针对 ID 进行无效检查 - 在不同的 Layout 文件中,使用了相同的 ID,或者删除了 ID ,它并不会提示空异常,导致增加了 App 的崩溃次数
- 仅仅支持 Kotlin
- 默认是通过
HashMap
缓存 ID 浪费空间,虽然可以通过在模块级build.gradle
文件内添加defaultCacheImplementation = "SPARSE_ARRAY"
来修改默认的实现方式为SparseArray
- ......
因此 ViewBinding 出现了,ViewBinding 解决了上述所有问题,ViewBinding 虽然好,但是也有它的不足之处。
- ViewBinding 相比于
kotlinx.android.synthetic
使用方式比较复杂 - 在
Activity
、Fragment
、Dialog
、Adapter
中 ViewBinding 和 DataBinding 初始化方式有些不同 - 需要单独处理
include
带merge
标签的布局,和不带merge
标签的布局等等 - DataBinding 结合 LiveData 一起使用需要做单独的处理
- ......
无论 ViewBinding 和 DataBinding 它们的使用方式都比较复杂,稍后我会介绍一种方法,只需要一行代码即可使用 ViewBinding(视图绑定) 和 DataBinding(数据绑定),那么 ViewBinding 和 DataBinding 有什么区别呢?
ViewBinding 和 DataBinding
ViewBinding:
- 仅仅支持绑定 View
- 不需要在布局文件中添加 layout 标签
- 需要在模块级
build.gradle
文件中添加viewBinding = true
即可使用 - 效率高于 DataBinding,因为避免了与数据绑定相关的开销和性能问题
- 相比于
kotlin-android-extensions
插件避免了空异常
DataBinding:
- 包含了 ViewBinding 所有的功能
- 需要在模块级
build.gradle
文件内添加dataBinding = true
并且需要在布局文件中添加 layout 标签才可以使用 - 支持 data 和 view 双向绑定
- 效率低于 ViewBinding,因为注释处理器会影响数据绑定的构建时间。
ViewBinding 可以实现的, DataBinding 都可以实现,但是 DataBinding 的性能低于 ViewBinding,DataBinding 和 ViewBinding 会为每个 XML 文件生成绑定类。
R.layout.activity_main -> ActivityMainBinding R.layout.fragment_main -> FragmentMainBinding R.layout.dialog_app -> DialogAppBinding
从 Android Studio 3.6
版本开始,就内置在 Gradle 插件中了,不需要添加任何额外的库来使用它们,但是在 Android Studio 3.6
和 Android Studio 4.0
中使用方式不一样。
// Android Studio 3.6 android { viewBinding { enabled = true } dataBinding{ enabled = true } } // Android Studio 4.0 android { buildFeatures { dataBinding = true viewBinding = true } }
接下来我们看一下如何在项目中使用 DataBinding 和 ViewBinding,因文章篇幅原因,这里仅仅演示在 Activity 中使用,更多用法可以查看 Binding 库的示例。
在模块级 build.gradle
文件内 开启 DataBinding 或者 ViewBinding 之后,需要在 Activity 中进行初始化,获取到 ViewBinding 实例即可使用。
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Viewbinding val binding: ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater) // DataBinding // val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main) with(binding){ textView.setText("Binding") } } }
如果在每个 Activity 中都需要添加 ActivityMainBinding.inflate()
或者 DataBindingUtil.setContentView()
方法来进行初始化,这样无疑增加了很多模板代码,不仅仅是 Activity 在 Fragment 、Dialog 、Adapter 中都需要添加对应的方法来初始化。
那么能不能用一种方法,可以统一这些初始化方案,在 Kotlin 中是可以实现的,仅仅需要一行代码即可实现 DataBinding 和 ViewBinding。
一行代码实现 DataBinding 和 ViewBinding
如果在每个 Activity
、 Fragment
、Dialog
、 Adapter
中都需要手动来添加相同的方法来初始化,这样的成本是非常大的,所以我们结合 Kotlin 委托属性,简化模板代码。
因此我推出了一个新库 Binding ,Binding 简化 DataBinding 和 ViewBinding 的使用, 只需要一行代码即可实现 DataBinding 和 ViewBinding,他们的实现方式并不相同。
Binding 未来的规划提供通用的 findViewById
解决方案,因技术的迭代更新从 butterknife 、 DataBinding 、 Kotlin 合成方法(Synthetic 视图)到现在 ViewBinding , 未来也有可能出现新的技术,无论技术怎么变化,只需要更新 Binding ,对外的使用保持不变。我们来看一下如何在项目中使用 Binding 库。
- 将下列代码添加在模块级
build.gradle
文件内,并且需要开启 DataBinding 或者 ViewBinding
dependencies { implementation 'com.hi-dhl:binding:1.0.4' }
- 在 Adapter(ListAdapter、PagingDataAdapter、RecyclerView.Adapter 等等)中使用 DataBinding 和 ViewBinding,添加
by viewbind()
或者by databind()
即可,示例如下所示,查看详细示例
class ProductViewHolder(view: View) : RecyclerView.ViewHolder(view) { // 通过 DataBinding 绑定的 itemView val binding: RecycleItemProductBinding by databind() fun bindData(data: Product?, position: Int) { binding.apply { product = data executePendingBindings() } } } class ProductViewHolderHeader(view: View) : RecyclerView.ViewHolder(view) { // ViewBinding val binding: RecycleItemProductHeaderBinding by viewbind() fun bindData(data: Product?, position: Int) { binding.apply { name.text = "通过 ViewBinding 绑定的 head" } } }
- 在
Activity
、AppCompatActivity
、FragmentActivity
中使用,继承对应的类添加by viewbind()
即可如下所示。
class MainActivity : AppCompatActivity() { // DataBinding val binding: ActivityMainBinding by databind(R.layout.activity_main) // ViewBinding val binding: ActivityMainBinding by viewbind() }
- 在
Fragment
中使用方式如下所示。
class MainFragment : Fragment(R.layout.fragment_main) { // DataBinding val binding: FragmentMainBinding by databind() // ViewBinding val binding: FragmentMainBinding by viewbind() }
- 在
Dialog
中使用方式如下所示。
class AppDialog(context: Context) : Dialog(context, R.style.AppDialog) { val binding: DialogAppBinding by viewbind() }
添加具有生命周期感知的 Dialog
class AppDialog(context: Context,lifecycle: Lifecycle) : Dialog(context, R.style.AppDialog) { val binding: DialogAppBinding by viewbind(lifecycle) }
更多详细的用法,可以前去仓库 Binding 查看,Binding 具有以下优点:
- 可以在
Activity
、AppCompatActivity
、FragmentActivity
、Fragment
、Dialog
、ListAdapter
、PagingDataAdapter
、RecyclerView.Adapter
中的使用 DataBinding 或者 ViewBinding - 简单的 API 只需要一行代码即可实现 DataBinding 或者 ViewBinding
- 避免大量的模板代码
- 避免内存泄露,具有生命周期感知能力,当生命周期处于
onDestroyed()
时会自动销毁数据
源码分析,将会在后续的文章中分享,如果这个仓库对你有帮助,请在仓库右上角帮我 star 一下,非常感谢。
如何迁移 Parcelable
Kotlin 将 Parcelable
相关的功能,移到了新的插件 kotlin-parcelize
,迁移只需要两步,如下所示。
- 在模块级
build.gradle
文件中,将kotlin-android-extensions
修改为kotlin-parcelize
- 将
import kotlinx.android.parcel.Parcelize
修改为import kotlinx.parcelize.Parcelize
但是这一步不是必须的,kotlinx.android.parcel.Parcelize
可以继续使用,到目前为止还没有发现什么问题(PS: 如果出现,只需要将包名替换就好)
结语
文章中相关代码,已经上传到 GitHub 欢迎前去仓库 Binding 查看
Binding 地址:https://github.com/hi-dhl/Binding
感谢 Simple one-liner ViewBinding in Fragments and Activities with Kotlin 文章带来的思路,以及从 Anko 、和 ViewBindingDelegate 等等开源库中学习到技巧。
陆陆续续有一些小伙伴们问我在哪里,可以看一些国外技术文章,所以我也给大家总结了一些不错的网站。
地址:https://site.51git.cn/gonav/2
全文到这里就结束了,如果有帮助 点个赞 就是对我最大的鼓励!!!
公众号:ByteCode,致力于分享最新技术原创文章,涉及 Kotlin、Jetpack、译文、系统源码、 LeetCode / 剑指 Offer / 多线程 / 国内外大厂算法题 等等一系列文章。
最后推荐我一直在更新维护的项目和网站:
- 全新系列视频:现代 Android 开发 (MAD) 技巧系列教程:在线查看
- 计划建立一个最全、最新的 AndroidX Jetpack 相关组件的实战项目 以及 相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,欢迎前去查看:AndroidX-Jetpack-Practice
- LeetCode / 剑指 offer / 国内外大厂面试题 / 多线程 题解,语言 Java 和 kotlin,包含多种解法、解题思路、时间复杂度、空间复杂度分析
- 最新 Android 10 源码分析系列文章,了解系统源码,不仅有助于分析问题,在面试过程中,对我们也是非常有帮助的,仓库持续更新,欢迎前去查看 Android10-Source-Analysis
- 整理和翻译一系列精选国外的技术文章,每篇文章都会有译者思考部分,对原文的更加深入的解读,仓库持续更新,欢迎前去查看 Technical-Article-Translation
- 「为互联网人而设计,国内国外名站导航」涵括新闻、体育、生活、娱乐、设计、产品、运营、前端开发、Android 开发等等网址,欢迎前去查看 为互联网人而设计导航网站
历史文章
- 为数不多的人知道的 Kotlin 技巧以及 原理解析(一)
- 为数不多的人知道的 Kotlin 技巧以及 原理解析(二)
- Jetpack 最新成员 AndroidX App Startup 实践以及原理分析
- Jetpack 成员 Paging3 实践以及源码分析(一)
- Jetpack 成员 Paging3 网络实践及原理分析(二)
- Jetpack成员Paging3获取网络分页数据并更新到数据库中(三)
- Jetpack 成员 Hilt 实践(一)启程过坑记
- Jetpack 成员 Hilt 结合 App Startup(二)进阶篇)进阶篇
- Jetpack 新成员 Hilt 与 Dagger 大不同(三)落地篇
- 全方面分析 Hilt 和 Koin 性能
- 神奇宝贝(PokemonGo) 眼前一亮的 Jetpack + MVVM 极简实战
- Kotlin Sealed 是什么?为什么 Google 都用
- Kotlin StateFlow 搜索功能的实践 DB + NetWork
- 再见吧 buildSrc, 拥抱 Composing builds 提升 Android 编译速度
- [Google] 再见 SharedPreferences 拥抱 Jetpack DataStore
- [译] Google 官方正解是否应该学习 Kotlin
- 官宣 有趣的 Android Studio 版本新方案