热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

AndroidJetpack之视图绑定ViewBinding

1.Jetpack简介手机厂商还没卷完Android12,Android13就悄悄声息地来了,间隔Google2008年9月22日公布Android1.0,已过来13个年头。历经1
文章目录[隐藏]
  • 1.Jetpack简介
  • 2.从手写findViewById 到ViewBinding
  • 3.ViewBinding根本用法
  • 4.封装优化思路
  • 5.原理
  • 6.一些补充

1.Jetpack简介

手机厂商还没卷完Android 12,Android 13就悄悄声息地来了,间隔Google 2008年9月22日公布Android 1.0,已过来13个年头。

历经13年的打磨和积淀,Android体系与社区生态已十分成熟,开发者从最后的框架少、没标准、代码都得本人写,到轮子、框架满天飞。得益于此,咱们少做了很多脏活累活(根底代码),把更多的工夫花在业务逻辑上,达成疾速迭代的目标。

但目不暇接的技术选型,也让开发者无从选择,以致于做出的利用参差不齐,Android官网始终没推出开发规范。而一些技术社区出于更高效地进行协同开发,逐步引入了MVP、MVVM等利用开发架构。应用这些架构开发出的利用,从我的项目品质、代码可读性与可维护性来说,都更加杰出,所以这些框架和技术逐步流行起来。

Google始终致力于Android生态环境的搭建,为了解决开发碎片化,不便宽广开发者,在2018年的 Google I/O大会上推出了全新的Android Jetpack利用开发架构。它是一套库、工具和指南的汇合,称作Jetpack开发工具集可能更贴切。

Android Jetpack 向后兼容,是为古代设计实际而设计的,如关注点拆散、测试能力、涣散耦合、观察者模式、管制翻转、Kotlin集成等生产力个性。旨在让开发者用更少的代码,更易构建出强壮、高质量的应用程序。

网上盛传的一张将Jetpack组件分为四大类的老图:

图片起源:

  • [Use Android Jetpack to Accelerate Your App Development ](
    https://android-developers.go…)

简略介绍下~

Architecture → 架构

帮忙开发者设计持重、可测试、易保护的利用。

  • Data Binding数据绑定,可应用申明式将布局中的界面组件绑定到利用中的数据源;
  • Lifecycles生命周期感知,可感知和响应Activity和Fragment的生命周期状态的变动;
  • LiveData可察看的数据持有者类,与惯例Observable不同,它是具备生命周期感知的;
  • Navigation利用内导航,Fragment的治理框架,或者说路由;
  • Paging列表分页,能够轻松实现分页预加载以达到有限滑动的成果;
  • Room轻量级ORM数据库,实质上是一个SQLite形象层,注解 + 编译时主动生成性能类;
  • ViewModel数据存储组件,具备生命周期感知能力;
  • WorkManager托管延时工作,即便APP被杀、或设施重启,只有TaskRecord还存在最近拜访列表中,都会执行;

Foundation → 根底

提供横向性能,如:向后兼容、测试、平安、Kotlin语言反对;

  • AppCompat→ 帮忙较低版本的Android零碎进行兼容;
  • Android KTX→ 基于Kotlin个性为Android、Jetpack提供一些繁难易用的扩大;
  • Multidex→ 为具备多个Dex文件利用提供反对;
  • Test→ 用于单元和运行时界面测试的 Android 测试框架;
  • Benchmark(性能检测)、Security(平安)等;

UI → 界面

  • Animation & Transition→ 内置动画及自定义动画成果;
  • Emoji→ 即使用户没有更新Android零碎也能够获取最新的表情符号;
  • Auto(车)、TV、WearOS;
  • Fragment→ 组件化界面的根本单位;
  • Layout→ 用XML中申明UI元素或者在代码中实例化UI元素;
  • Paletee→ 从调色板中提取出有用的信息;

Behavior → 行为

  • Download Manager→ 解决长时间运行的HTTP下载、超时重连的零碎服务;
  • Media & Playback→ 用于媒体播放和路由(包含 Google Cast)的向后兼容 API;
  • Permissions→ 用于检查和申请利用权限的兼容性API;
  • Notifications→ 提供向后兼容的告诉API,反对Wear和Auto;
  • Sharing→ 提供适宜利用操作栏的共享操作;
  • Slices→ 一种UI模板,创立可在养分内部显示利用数据的灵便界面元素;

尽管说,Android官网曾经找不到下面这个图了,猜想官网旨在强化Architecture架构组件,其余三个只是对已有内容的收集整理。理论开发中,也是这部分的组件用得多一些,Jetpack库可独自应用,也能够组合应用,开发者可按需抉择。对此,官网还进行了更粗疏的分类,具体可见:

  • [ 《按类型摸索Jetpack库》 ](
    https://developer.android.com…)

对于Jetpack的简介就到这里,在选型时弄清楚组件的存在原因、责任边界,就能对症下药。本节开始折腾,先带来一个超简略的 → ViewBinding(视图绑定)

2.从手写findViewById 到ViewBinding

从晚期对照XML手写findViewById,到在线工具主动生成:

到AS插件主动生成:

再到View注入框架 ↓

  • ButterKnife(黄油刀)

前面Kotlin遍及,带来了扩大创立kotlin-android-extensions(KAE)间接拿id当控件用,原理:

类中定义一个存储控件援用的HashMap,id为key,控件实例为value,当用到控件时,先查HashMap中该id对应的实例是否缓存,是返回,否findViewById获取实例存到HashMap中,同时把找到的实例返回。

粗犷的空间换工夫,不便是挺不便的,但也存在下述问题:

好景不长,Kotlin 1.4.20-M2中,JetBrains废除了KAE,转而倡议咱们应用ViewBinding

3.ViewBinding根本用法

ViewBinding的作用:代替findViewById,还能够保障空平安和类型平安,反对Java。

:应用ViewBinding,AGP版本需 >= 3.6

接着介绍下根本用法,局部内容搬运自官网文档:

  • [《视图绑定》 ](
    https://developer.android.com…)

① 启用ViewBinding

须要启用视图绑定的Module,在其build.gradle增加下述配置:

android {       ...       viewBinding {           enabled = true       }   }   

不须要生成绑定类的布局XML文件,可在根节点中增加下述属性:

       ...      

编译后,AGP会为Module中蕴含的XML布局文件生成一个绑定类,类名规定:

XML文件名转换为Pascal大小写,并加上Binding,比方:result\_profile.xml → ResultProfileBinding。

② 三个类绑定API

// View已存在   fun    bind(view : View) : T      // View未存在   fun    inflate(inflater : LayoutInflater) : T   fun    inflate(inflater : LayoutInflater, parent : ViewGroup?, attachToParent : Boolean) : T   

接下来演示一波各种场景下的ViewBinding用法,其实都是围绕上述三个API进行的~

③ Activity

class  MainActivity : AppCompatActivity() {       private lateinit var binding: ActivityMainBinding          override  fun  onCreate(savedInstanceState: Bundle?) {           super.onCreate(savedInstanceState)           // 1、实例化绑定实例           binding = ActivityMainBinding.inflate(layoutInflater)           // 2、取得对根视图的援用           val view = binding.root           // 3、让根视图称为屏幕上的流动视图           setContentView(view)           // 4、援用视图控件           binding.tvContent.text = "批改TextView文本"       }   }   

④ Fragment

class  ContentFragment: Fragment() {       private  var _binding: FragmentContentBinding? = null       private  val binding get() = _binding!!          override  fun  onCreateView(           inflater: LayoutInflater,           container: ViewGroup?,           savedInstanceState: Bundle?       ): View {           _binding = FragmentContentBinding.inflate(inflater, container, false)           return binding.root       }          override  fun  onViewCreated(view: View, savedInstanceState: Bundle?) {           super.onViewCreated(view, savedInstanceState)           binding.ivLogo.visibility = View.GONE       }          override  fun  onDestroyView() {           super.onDestroyView()           // Fragment的存活工夫比View长,务必在此办法中革除对绑定类实例的所有援用           // 否则会引发内存泄露           _binding = null       }   }   

如果布局已inflated,还能够采纳另一种写法(调bind):

class  TestFragment: Fragment(R.layout.fragment_content) {       private var _binding: FragmentContentBinding? = null          override  fun  onViewCreated(view: View, savedInstanceState: Bundle?) {           super.onViewCreated(view, savedInstanceState)           val binding = FragmentContentBinding.bind(view)           _binding = binding           binding.ivLogo.visibility = View.VISIBLE       }          override  fun  onDestroyView() {           super.onDestroyView()           // 同样须要置空           _binding = null       }   }   

⑤ Dialog

如果是继承DialogFragment写法同Fragment,如果是继承Dialog写法示例如下(PopupWindow相似)~

class  TestDialog(context: Context) : Dialog(context) {       override  fun  onCreate(savedInstanceState: Bundle?) {           super.onCreate(savedInstanceState)           val binding = DialogTestBinding.inflate(layoutInflater)           setContentView(binding.root)           binding.tvTitle.text = "对话框题目"       }   }   

⑥ RecyclerView

class  TestAdapter(list: List) : RecyclerView.Adapter() {       private  var mList: List = list          override  fun  onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {           // 需在此初始化以取得父类容器           val binding = ItemTestBinding.inflate(LayoutInflater.from(parent.context), parent, false)           return ViewHolder(binding)       }          override  fun  onBindViewHolder(holder: ViewHolder, position: Int) {           holder.tvItem.text = "Adapter"       }          override  fun  getItemCount() = mList.size          // 传递Binding对象       class  ViewHolder(binding: ItemTestBinding) : RecyclerView.ViewHolder(binding.root) {           var tvItem: TextView = binding.tvItem       }   }   

⑦ 自定义ViewGroup

ViewGroup子类能力应用视图绑定,View子类不可应用,示例如下:

class  TestLayout: LinearLayout {       constructor(context: Context): super(context)       constructor(context: Context, attrs: AttributeSet): super(context, attrs) {           val inflater = LayoutInflater.from(this.context)           val binding = ItemLayoutBinding.inflate(inflater, this, true)           binding.tvLayout.text = "自定义ViewGroup"       }          override fun onDraw(canvas: Canvas?) {           super.onDraw(canvas)       }   }   

⑧ include

依据include的布局xml是否带标签,分为两种,先是不带的状况: include的xml文件名为sub\_include\_test.xml,id为include\_layout:

而后是带的状况,布局文件改下:

应用局部的代码不变,运行奔溃报错信息如下:

起因是merge并不会加载到布局里,解法:把include标签的id去掉,而后bind传入父布局~

⑨ ViewStub

根底用法很简略,也很好上手,但存在下述问题:

需反复编写:创立和回收ViewBinding实例的样板代码,特地是Fragment,还要手动置空。

所以有必要封装优化一波~

4.封装优化思路

① 泛型 + 父类实现模板代码

最容易想到的惯例写法,配合泛型,把模板代码都在父类中写好,非常简单:

abstract  class  BaseFragment(layoutId: Int) : Fragment(layoutId) {       private  var _binding: T? = null       val binding get() = _binding!!          override  fun  onViewCreated(view: View, savedInstanceState: Bundle?) {           super.onViewCreated(view, savedInstanceState)           _binding = initBinding(view)           init()       }          abstract  fun  initBinding(view: View): T          abstract  fun  init()          override  fun  onDestroyView() {           _binding = null           super.onDestroyView()       }   }      // 子类实现   class  TestFragment : BaseFragment(R.layout.fragment_content) {       override  fun  initBinding(view: View) = FragmentContentBinding.bind(view)          override  fun  init() {           binding.ivLogo.visibility = View.VISIBLE       }      }   

② Kotlin委托 + lifecycle组件

有些敌人可能感觉写在父类中侵入性太强,接着试下用其余形式进行封装,先看原始Activity:

要把圈住的代码干掉,先是泛型传递问题,泛型在进JVM前会被擦除,可在运行时通过反射取得,还能够通过实例化类类型代替类援用,如:

fun  FragmentActivity.startActivity(context: Context, clazz: Class) {       startActivity(Intent(context, clazz))   }      // 调用处   startActivity(context, MainActivity::class.java)   

而在Kotlin中还能够用inline定义一个内联函数(编译时主动替换到调用地位),配合reified具体化(类型不擦除),失去泛型类型的Class,如:

inline  fun  Activity.startActivity(context: Context) {       startActivity(Intent(context, T::class.java))   }      // 调用   startActivity(context)   

能够,配合反射invoke()调用,顺手写上一个扩大办法:

调用下:

看似探囊取物,后果一跑就崩:

不过也在预料之内,Activity还没onCreate()就初始化了,不空才怪,能够利用规范委托-lazy提早初始化,批改后的代码:

调用下:

运行通过,你还能够把还能够把setContentView()也塞到扩大中:

配合lifecycle组件,棘手把Fragment的也写进去:

调用下:

对了,如果还不想应用反射,能够利用Kotlin高阶函数,示例如下:

调用下:

啧啧啧,你还能够不必lazy,本人重写ReadOnlyProperty,这里只是试试水封装,并没用到生产上,更欠缺的封装计划可自行参考下述开源库:

  • [ Binding ](
    https://github.com/hi-dhl/Bin…)
  • [ VBHelper ](
    https://github.com/jaydroid10…)

5.原理

AGP会为模块中每个XML生成一个绑定类,该类的实例会间接援用布局中申明了资源id的View

① 主动生成的绑定类

关上:module模块名/build/generated/intermediates/javac/渠道/包名/databinding

能够看到 (基于AGP 7.1.1,不同AGP版本可能不一样):

主动生成的class文件,顺手关上一个:

所以实质上还是findViewById,只是主动生成了控件实例,并一一对应,接着简略理解下大略的生成流程。

② 生成Java类

执行gradlew assembleDebug,在Task构建列表没找到ViewBinding,却找到了DataBinding:

关上AGP源码,全局搜dataBindingMergeGenClassesDataBindingMergeBaseClassLogTask.kt

跟到:TaskManager.ktcreateDataBindingTasksIfNecessary

2333,跟DataBinding混一起了,所以ViewBinding其实只是DataBinding性能的一小部分~

看回:DataBindingMergeBaseClassLogTask,增量和全量执行动作:

跟下:DataBindingMergeBaseClassLogDelegate

跟下:DataBindingMergeBaseClassLogRunnable

判断文件是否新建、批改、或移除,跟下是哪个文件:

全局搜下这个结尾的文件,在下述目录找到了它:

不难看出是:XML名称和ViewBinding类的映射,往下看DataBindingMergeDependencyArtifactsTask,BR相干的,目前不晓得是干嘛的。再往下走:DataBindingGenBaseClassesTask → CreationAction

跟下:DataBindingGenBaseClassesTask → @TaskAction

先看buildInputArgs(),构建输出参数,同样对增量和全量编译进行了不同的解决,而后返回配置实例

接着看CodeGenerator类,见名知意,代码生成器

这里间接索引不到BaseDataBinder,须要另外依赖:databinding-compiler-common

implementation 'androidx.databinding:databinding-compiler-common:7.1.0'   

async后就能够了,关上

  • [ MVNResponsitory – Gradle » 7.1.0 ](
    https://mvnrepository.com/art…)

能够看到它依赖了47个运行时库~

跟下:BaseDataBinder → generateAll()

跟下:ViewBinderGenerateJava.kt → toJavaFile() → JavaFileGenerator

Java文件就是从这里结构进去的,具体结构过程,感兴趣的能够本人翻阅下此文件。

另外,如果你想理解布局采集和写Layout局部的逻辑,能够参考

  • [《ViewBinding 的实质》 ](
    https://juejin.cn/post/684490…)

笔者卷不动了…

6.一些补充

① 与DataBinding的区别

能够把ViewBinding看做DataBinding性能的子集,它有的性能DataBinding都有,不须要数据绑定单纯想代替findViewById能够用ViewBinding。

② 不必build就能主动生成Java类

笔者猜想:AS起了一个过程Filesystem events processor用于监听文件变动,有文件变动时回调执行ViewBinding相干的Task。

③ KAE库过期,迁徙Parcelable

Module档次的build.gradle增加kotlin-parcelize插件。

转自: 抠腚男孩

以上就是本节的全部内容,有疑难或补充欢送评论区指出,谢谢~



推荐阅读
  • H5技术实现经典游戏《贪吃蛇》
    本文将分享一个使用HTML5技术实现的经典小游戏——《贪吃蛇》。通过H5技术,我们将探讨如何构建这款游戏的两种主要玩法:积分闯关和无尽模式。 ... [详细]
  • CRZ.im:一款极简的网址缩短服务及其安装指南
    本文介绍了一款名为CRZ.im的极简网址缩短服务,该服务采用PHP和SQLite开发,体积小巧,约10KB。本文还提供了详细的安装步骤,包括环境配置、域名解析及Nginx伪静态设置。 ... [详细]
  • 本文探讨了如何在PHP与MySQL环境中实现高效的分页查询,包括基本的分页实现、性能优化技巧以及高级的分页策略。 ... [详细]
  • 如何将955万数据表的17秒SQL查询优化至300毫秒
    本文详细介绍了通过优化SQL查询策略,成功将一张包含955万条记录的财务流水表的查询时间从17秒缩短至300毫秒的方法。文章不仅提供了具体的SQL优化技巧,还深入探讨了背后的数据库原理。 ... [详细]
  • Docker安全策略与管理
    本文探讨了Docker的安全挑战、核心安全特性及其管理策略,旨在帮助读者深入理解Docker安全机制,并提供实用的安全管理建议。 ... [详细]
  • 二维码的实现与应用
    本文介绍了二维码的基本概念、分类及其优缺点,并详细描述了如何使用Java编程语言结合第三方库(如ZXing和qrcode.jar)来实现二维码的生成与解析。 ... [详细]
  • 入门指南:使用FastRPC技术连接Qualcomm Hexagon DSP
    本文旨在为初学者提供关于如何使用FastRPC技术连接Qualcomm Hexagon DSP的基础知识。FastRPC技术允许开发者在本地客户端实现远程调用,从而简化Hexagon DSP的开发和调试过程。 ... [详细]
  • Python 领跑!2019年2月编程语言排名更新
    根据最新的编程语言流行指数(PYPL)排行榜,Python 在2019年2月的份额达到了26.42%,稳坐榜首位置。 ... [详细]
  • 从理想主义者的内心深处萌发的技术信仰,推动了云原生技术在全球范围内的快速发展。本文将带你深入了解阿里巴巴在开源领域的贡献与成就。 ... [详细]
  • 回顾两年前春节期间的一个个人项目,该项目原本计划参加竞赛,但最终作为练习项目完成。独自完成了从编码到UI设计的全部工作,尽管代码量不大,但仍有一定的参考价值。本文将详细介绍该项目的背景、功能及技术实现。 ... [详细]
  • 小编给大家分享一下Vue3中如何提高开发效率,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获, ... [详细]
  • 实践指南:使用Express、Create React App与MongoDB搭建React开发环境
    本文详细介绍了如何利用Express、Create React App和MongoDB构建一个高效的React应用开发环境,旨在为开发者提供一套完整的解决方案,包括环境搭建、数据模拟及前后端交互。 ... [详细]
  • 2017年软件开发领域的七大变革
    随着技术的不断进步,2017年对软件开发人员而言将充满挑战与机遇。本文探讨了开发人员需要适应的七个关键变化,包括人工智能、聊天机器人、容器技术、应用程序版本控制、云测试环境、大众开发者崛起以及系统管理的云迁移。 ... [详细]
  • 探讨低代码行业发展现状,分析其未能催生大型企业的原因,包括市场需求、技术局限及商业模型等方面。 ... [详细]
  • c#  项目文件,C#viual studio使用方法
    一、项目文件1)Properties节点下主要存放的是当前程序集相关的信息,如版本号、标题等。双击”Properties“,打开如下项目属 ... [详细]
author-avatar
奋斗的小鸟
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有