本文主要是总结各种视图绑定方案,简单介绍原理和优缺点,作为工作总结、技术整理以及分享培训使用
本文大纲如下:
本文概述:
全文总结对比,关于视图绑定目前相对比较好的方式是viewbinding, 如果你的项目深入使用了jetpack,想用databinding那么就不需要使用viewbinding了,详细请看下面具体章节:
一、Android基础API -> findViewById
1.1 这个方法不多说后面会提到,大家都会用;
- 需要注意下布局层次的控制,页面绘制的核心是循环遍历id去绘制, 因此布局时要求布局层次清晰的同时尽量&&不要指数级的增加布局层次,遍历核心代码见下图:
1.2 布局效率可以参考如下文章:
二、ButterKnife
2.1 仓库地址:
github.com/JakeWharton… 说明了如果集成butterknife等等
2.2 实现原理:annotationProcessor/Kapt预编译注解处理器
2.3 优点:简化findViewById的繁重工作量和异常保护
- 另外使用butterKnife的As插件可以一键生成xml中的id对应的注解字段,不需要开发手动写注解对应关系,进一步提高开发效率
2.4 总结:已经如此优秀,经营这么多年,为什么主动放弃继续维护?
2.4.1 从实现原理来说,相比最新的方案viewbinding核心原理没有太落后也没有更高级!
2.4.2 ButterKnife自身多版本项目升级切换的成本也是个问题,从低版本升级到高版本涉及到语法的更新不支持无感升级等开源库通病,导致很多开发者干脆坚持使用老版本老项目不再更新,即便如此依然被无数开发者用了5年以上了。
2.4.3 因为多个后浪包括google官方已经站在巨人的肩膀上实现了对于ButterKnife完全超越,集成更简单,集成的库更轻量级,同时使用更简单,后面会对比说明,ButterKnife的开发者也是意识到了这个问题所以放弃维护了😢,感谢黄油刀陪伴我们这么多年,希望ButterKnife团队能够给开源社区贡献更多好的东西!
三、 Kotlin Android Extensions
Google大力推动了Android 开发向Kotlin迁移,Kotlin由JetBrains负责开发,所以他们主推的KTX插件也被当成是解决视图绑定的方式之一。你知道每天都用的Android Studio开发工具是哪个公司开发的吗❓
优点:使用简单,ALT+Enter按照提升引入文件即可!
缺点:因为太过简单,更新效率更高,反而导致了问题太多如下:
总结:引入他可能导致的问题,已经让开发者忘记了他的优点,个人Kotlin小项目也可以使用,但是相比View binding几乎没有啥优点。
四、 viewbinding
4.1 概念背书:
Google主推的Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者精力集中编写重要的代码, viewBinding是Jetpack中的一个组件,入手相对简单
4.2 集成和设置:
4.2.1 使用要求
视图绑定在 Android Studio 3.6 Canary 11 及更高版本中可用。
4.2.2 开启视图绑定
android {
viewBinding {
enabled = true
}
}
4.2.3 忽略某个布局文件的视图绑定
<LinearLayout
tools:viewBindingIgnore="true" >
</LinearLayout>
4.3 使用说明:
4.3.1 XML文件: activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:background="@drawable/background"
tools:context=".main.MainActivity">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tablayout"
style="@style/tabLayoutStyle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tabMaxWidth="200dp"
app:tabMinWidth="50dp"
app:tabMode="scrollable"></com.google.android.material.tabs.TabLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
4.3.2 自动生成绑定类文件:
4.3.3 使用Binding生成类直接setContentView加载,默认推荐方式
//获取绑定类实例
binding = ActivityMainBinding.inflate(layoutInflater)
//通过绑定类获取布局后,传给setContentView设置内容视图
setContentView(binding.root)
//通过绑定类获取对应的ID视图,进行操作
binding.tablayout.setupWithViewPager(binding.viewpaper);
4.3.4 基类封装使用,更简洁,无需开发者关心配置视图代码,基类jvm反射方式加载
- Fragment使用:
- Activity使用:
- Activity基类代码封装:
public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActivity {
protected T binding;
private String mClassName = BaseActivity.class.getSimpleName();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Type superclass = getClass().getGenericSuperclass();
Class<?> aClass = (Class<?>) ((ParameterizedType) superclass).getActualTypeArguments()[0];
try {
Method method = aClass.getDeclaredMethod("inflate", LayoutInflater.class);
binding = (T) method.invoke(null, getLayoutInflater());
setContentView(binding.getRoot());
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
// Logger.error(mClassName, " onCreate Error");
e.printStackTrace();
}
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
initViews();
initDatas();
}
/**
* 初始化布局View
*/
protected abstract void initViews();
/**
* 初始化数据
*/
protected abstract void initDatas();
}
4.4 原理分析:
- viewbinding采用预编译的方式自动生成了binding代码
- 这个inflate函数你是否还有印象🤔
- 核心是bind函数,bind函数执行过程中return了我们的ActivityMainBinding对象,同时你是否注意到了,它还做了空安全检查😘
- bind函数源码
@NonNull
public static ActivityMainBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId: {
id = R.id.line;
View line = rootView.findViewById(id);
if (line == null) {
break missingId;
}
id = R.id.tablayout;
TabLayout tablayout = rootView.findViewById(id);
if (tablayout == null) {
break missingId;
}
id = R.id.viewpaper;
ViewPager viewpaper = rootView.findViewById(id);
if (viewpaper == null) {
break missingId;
}
return new ActivityMainBinding((ConstraintLayout) rootView, line, tablayout, viewpaper);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
4.5 精简代码
- 这是我们常见的view定义
- 这是我们常见的view初始化
- 这是使用butterknife的view定义和初始化
- 这是使用viewBinding,view只需要在xml中定义好,然后直接使用
- 会不会觉得哪里不对,想反驳,我可以不定义view变量findviewbyid直接使用,那使用的时候是否需要强制转换呢,否则能.出方法嘛!以及你真的能够找到属于当前页面的id嘛!,这些viewbinding都可以😇
4.5 总结:
viewbingding优点
- 集成简单,使用方便,基本不侵入开发者的代码(他只是额外生成你可以忽略的代码, 如果你不去探究他的实现,基本可以忽略了)
- 空安全检查,防止我们未初始化导致的空指针异常,谁都会犯错,使用框架的好处就是尽量规避犯错的场景,规避低级别的Bug(你有没有犯过类似的错误,发现后拍大腿呢😛)!
- 降低代码量和提升开发效率,页面使用viewbinding可以降低几十到几百行的变量定义和初始化的函数,大大提升了开发不必要的重复工作量
- 简化代码的可读性提升可读性,可读性提升本身也会提升代码质量,因为复杂页面大量定义的变量和命名规范实在让人头疼,初始化的cv工作也让人忍无可忍! 综合以上优点,对于UI页面元素复杂的页面,从编码到减少空指针bug,提升代码质量等综合考量,个人开发多个APP页面总结平均可以提高每个页面纯UI开发20%左右的开发效率(特别简单静态页面除外)
五、 databinding
5.1 概念区别:
5.2 区别介绍:
- 使用视图绑定时,布局不需要布局标签
- 您不能使用viewbinding将布局与xml中的数据绑定(没有绑定表达式,无绑定)
- 视图绑定的主要优点是速度和效率。它具有较短的构建时间,因为它避免了由于注释处理器影响数据绑定的构建时间而导致的与数据绑定相关的开销和性能问题。
- 简而言之,没有任何视图绑定可以做数据绑定不能做的事情(尽管以更长的构建时间为代价),但是有很多数据绑定可以做的视图绑定不能做。
缺点:
- 这里面也引申出了黄油刀放弃维护的根本原因,基于效率对比他不如Viewbingding视图绑定高效,基于注解处理器他没有解决数据绑定的问题不如DataBinding...(有点诺基亚老大哥的感觉,从万众瞩目,突然就不行了)
5.3 简单图示总结:
六、敲黑板,视图绑定总结
- 如果你不想过多的引入Jetpack到你的项目中,那你单纯的使用viewBinding就好
- 如果你喜欢研究框架并且公司其他同事也支持配合,那可以考虑综合引入Jetpack套件
七、Jetpack扩展
- viewBinding 解决视图绑定的问题,提升页面开发效率和质量
- lifeCycle 高效的解决业务逻辑类的无生命周期问题
- viewModel 真实的数据存储仓库,解决横竖屏转换等场景走onDestory销毁时序数据丢失的问题,除非你的页面真正的销毁了(想深入了解可以查一下viewModel生命周期)
- Livedata 让你的数据动态可观察,自带观察者模式,让数据监控更轻松
- dataBinding xml的数据绑定,结合Livedata实现数据在逻辑和页面间的双向绑定(类似vue、react等跨平台开发中的双向绑定)(但是google 又在推动研发去xml化的Compose技术所有你如果没有正在使用dataBinding也可以不去集成了,Compose的优化让人拭目以待,但是目前还不太方便使用)
Jetpack扩展总结:
我理解lifeCycle+viewModel+viewBinding是Jetpack基础组件,能够大量提升开发效率和质量,安全可靠,对于成熟Android开发者学习和入手成本相对不高; 如果框架搭建中我们决定了深入集成Jetpack,实际上Livedata+dataBinding是几乎绑定组入的必然选择,简单介绍,后续会结合源码和模板代码通过新文章分享
双手合十,收工!!!
以上 南院大王(For The Dream)