新技术 ViewBinding 最佳实践 & 原理击穿
本文作者:CoderPig,原文发布于:抠腚男孩。
https://android-developers.googleblog.com/2018/05/use-android-jetpack-to-accelerate-your.html
简单介绍下~Architecture → 架构
• Data Binding→数据绑定,可使用声明式将布局中的界面组件绑定到应用中的数据源;
• Lifecycles→生命周期感知,可感知和响应Activity和Fragment的生命周期状态的变化;
• LiveData→可观察的数据持有者类,与常规Observable不同,它是具有生命周期感知的;
• Navigation→应用内导航,Fragment的管理框架,或者说路由;
• Paging→列表分页,可以轻松实现分页预加载以达到无限滑动的效果;
• Room→轻量级ORM数据库,本质上是一个SQLite抽象层,注解 + 编译时自动生成功能类;
• ViewModel→数据存储组件,具备生命周期感知能力;
• WorkManager→托管延时任务,即使APP被杀、或设备重启,只要TaskRecord还存在最近访问列表中,都会执行;
Foundation → 基础
• 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模板,创建可在营养外部显示应用数据的灵活界面元素;
https://developer.android.com/jetpack/androidx/explorer?case=all
https://github.com/JakeWharton/butterknife
类中定义一个存储控件引用的HashMap,id为key,控件实例为value,当用到控件时,先查HashMap中该id对应的实例是否缓存,是返回,否findViewById获取实例存到HashMap中,同时把找到的实例返回。
https://developer.android.com/topic/libraries/view-binding?hl=zh-cn
① 启用ViewBinding
android {
...
viewBinding {
enabled = true
}
}
<LinearLayout
...
tools:viewBindingIgnore="true" >
...
</LinearLayout>
XML文件名转换为Pascal大小写,并加上Binding,比如:result_profile.xml → ResultProfileBinding。
② 三个类绑定API
// View已存在
fun <T> bind(view : View) : T// View未存在
fun <T> inflate(inflater : LayoutInflater) : T
fun <T> inflate(inflater : LayoutInflater, parent : ViewGroup?, attachToParent : Boolean) : T
③ 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
}
}
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
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<String>) : RecyclerView.Adapter<TestAdapter.ViewHolder>() {
private var mList: List<String> = 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
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
⑨ ViewStub
需重复编写:创建和回收ViewBinding实例的样板代码,特别是Fragment,还要手动置空。
① 泛型 + 父类实现模板代码
abstract class BaseFragment<T : ViewBinding>(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<FragmentContentBinding>(R.layout.fragment_content) {
override fun initBinding(view: View) = FragmentContentBinding.bind(view)
override fun init() {
binding.ivLogo.visibility = View.VISIBLE
}
}
② Kotlin委托 + lifecycle组件
fun <T: Activity> FragmentActivity.startActivity(context: Context, clazz: Class<T>) {
startActivity(Intent(context, clazz))
}// 调用处
startActivity(context, MainActivity::class.java)
inline fun <reified T : Activity> Activity.startActivity(context: Context) {
startActivity(Intent(context, T::class.java))
}// 调用
startActivity<MainActivity>(context)
Binding
https://github.com/hi-dhl/Binding
VBHelper
https://github.com/jaydroid1024/anyby
AGP会为模块中每个XML生成一个绑定类,该类的实例会直接引用布局中声明了资源id的View
① 自动生成的绑定类
② 生成Java类
implementation 'androidx.databinding:databinding-compiler-common:7.1.0'
https://mvnrepository.com/artifact/com.android.tools.build/gradle/7.1.0
https://juejin.cn/post/6844904106268557326
笔者卷不动了...① 与DataBinding的区别
可以把ViewBinding看做DataBinding功能的子集,它有的功能DataBinding都有,不需要数据绑定,单纯想替代findViewById可以用ViewBinding。
② 不用build就能自动生成Java类
笔者猜测:AS起了一个进程Filesystem events processor用于监听文件变化,有文件变动时回调执行ViewBinding相关的Task。
③ KAE库过时,迁移Parcelable
Module层次的build.gradle添加kotlin-parcelize插件。
是时候更新手里的武器了—Jetpack最全简析
https://www.cnblogs.com/jimuzz/p/13821376.html
Android Jetpack Architecture Components
https://medium.com/kayvan-kaseb/android-jetpack-architecture-components-119c9c973d7e
是时候拥抱ViewBinding了!!
https://blog.csdn.net/u010976213/article/details/104501830
Android | ViewBinding 与 Kotlin 委托双剑合璧
https://juejin.cn/post/6960914424865488932
ViewBinding 巧妙的封装思路,还能这样适配 BRVAH
https://juejin.cn/post/6950530267547172901
Android View Binding使用详解
https://juejin.cn/post/6995069102041595940
Kotlin 插件的落幕,ViewBinding 的崛起
https://juejin.cn/post/6905942568467759111
ViewBinding 基类编写
https://juejin.cn/post/6951691260314779655
译:深入研究ViewBinding 在 include, merge, adapter, fragment, activity 中使用
https://juejin.cn/post/6844904065655111693
ViewBinding 的本质
https://juejin.cn/post/6844904106268557326