原文:Migrating the deprecated Kotlin Android Extensions compiler plugin
作者:Ahmad El-Melegy


在Kotlin 1.4.20-M2中,JetBrains废弃了Kotlin Android Extensions编译插件。

其实这是早就预料到的,你可以在这次提交中看到详情。

kotlinx.android.synthetic不再是一个推荐的做法。删除了显式的findViewById。

但为什么呢?

kotlinx的合成属性存在一些众所周知的问题。

  • 它公开了以view的id为名的全局变量,但该名称与实际的布局无关,没有针对无效查找进行检查。
  • 它只适用于Kotlin。
  • 当View只存在于某些配置中时,它们没有空安全提示。
  • 所有这些问题加在一起,导致增加了Android应用的崩溃次数。
  • 另外谷歌正在推广模块化,但合成属性不能跨模块工作。这是自2018年1月以来的一个公开问题。

有哪些替代方案?

  • View Binding是视图查找以及绑定的推荐方案,但与Android Kotlin Extensions相比,它确实增加了一些开销。但它增加了编译时对视图查找的检查和类型安全。
  • 传统方式findViewById,Kotlin和Java都适用。

JetBrains废弃了Kotlin Android Extensions,推荐使用View Binding,所以我们将在本文中探讨如何迁移到View Binding。

View Binding

不要与Data Binding混淆

View Binding是一种功能,它允许您更容易地编写与视图交互的代码。

一旦在一个模块中启用了View Binding,它就会为该模块中存在的每个 XML 布局文件生成一个绑定类。

绑定类的实例包含对相应布局中具有ID的所有VIew的直接引用。

View Binding对于在多个配置中定义的布局来说是Null-safe的。

View Binding将检测视图是否只存在于某些配置中,并创建一个@Nullable属性。

View Binding适用于Java和Kotlin。

如何启用View Binding?

你不需要添加任何额外的库来启用View Binding。从Android Studio 3.6版本开始,它就被内置到Android Gradle Plugin中了。如果要在模块中启用该功能,请在你的build.gradle文件中添加以下内容。

android {...buildFeatures {viewBinding true}
}

如何使用View Binding?

如果为模块启用了View Binding,则会为模块包含的每个 XML 布局文件生成一个绑定类。

每个绑定类都包含对根视图和所有具有ID的视图的引用。

绑定类的名称是通过将 XML 文件的名称转换为驼峰式大小写,并在结尾处添加 Binding 一词来生成的。

**译者注:**例如,假设某个布局文件的名称为 result_profile.xml,所生成的绑定类的名称就为 ResultProfileBinding

在Activity中使用View Binding

private lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)
}

然后可以使用binding对象访问视图:

binding.name.text = "Some Text"

在Fragment中使用View Binding

Fragment中使用View Binding需要多加注意,如果使用不当它会引发内存泄漏,如果你没有在onDestroy中将view置空,那么它就不会从内存中清除。

private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?
): View? {_binding = FragmentMainBinding.inflate(inflater, container, false)return binding.root
}override fun onDestroyView() {super.onDestroyView()_binding = null
}

然后就可以像我们在Activity中那样使用它。

binding.name.text = "Some Text"

原理

View Binding将为模块中的每个XML布局生成一个绑定对象。

例如这个activity_main.xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayoutxmlns: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:context=".MainActivity"><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

View Binding将生成ActivityMainBinding.java

public final class ActivityMainBinding implements ViewBinding {@NonNullprivate final ConstraintLayout rootView;@NonNullpublic final TextView textView;

View Binding将为每个具有id的视图生成一个正确类型的属性。它还会生成一个名为rootView的属性。
视图绑定对Kotlin是友好的,因为所有的属性都被注解为@Nullable@NonNull,Kotlin知道如何将它们暴露为空安全类型。

@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {return inflate(inflater, null, false);
}@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,@Nullable ViewGroup parent, boolean attachToParent) {View root = inflater.inflate(R.layout.activity_main, parent, false);if (attachToParent) {parent.addView(root);}return bind(root);
}

ActivityMainBinding.java中,视图绑定会生成一个公共的inflate方法。

它调用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.textView;TextView textView = rootView.findViewById(id);if (textView == null) {break missingId;}return new ActivityMainBinding((ConstraintLayout) rootView, textView);}String missingId = rootView.getResources().getResourceName(id);throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}

bind方法中,生成的绑定对象将为每个要绑定的View调用findViewById

那么Kotlin Android Extensions的Parcelize特性呢?

不要忘了,Kotlin中的Parcelize功能是kotlin-android-extensions编译器插件的一部分,所以如果你的Parcelable类依赖于Parcelize注解,那么移除该插件将使它们无法编译。

JetBrains将Kotlin Android Extensions中的Parcelize提取到一个新的插件kotlin-parcelize中。

首先你需要在你的模块中添加kotlin-parcelize插件。

plugins {..id 'kotlin-parcelize'
}

然后更改旧的import语句,将:

import kotlinx.android.parcel.Parcelize

改为:

import kotlinx.parcelize.Parcelize

例子:

import kotlinx.parcelize.Parcelize
import android.os.Parcelable@Parcelize
class User(val name: String, val age: Int): Parcelable

你可能会发现这个IDE错误

Class ‘User’ is not abstract and does not implement abstract member public abstract fun describeContents(): Int defined in android.os.Parcelable

别担心,你的应用会很好地构建,这是一个误报的错误,已经在youtrack上被报告,并被修复,所以应该在下一个版本中发布。

请注意,这个插件只能1.4.20-M2版本开始使用,这个版本也是废弃kotlin-android-extensions编译器插件的版本。

如果你想尝试一下,你需要在IntelliJ IDEA或Android Studio安装Kotlin EAP 插件 。

  • 选择ToolsKotlinConfigure Kotlin Plugin Updates
  • Update channel 列表中,选择Early Access Preview 1.4.x频道
  • 点击Check again.
  • 然后点击Install

太长不看

这是从kotlin-android-extensions插件迁移到ViewBindingkotlin-parcelize插件时应该做的事情:

  • kotlin-android-extensions插件从build.gradle文件中删除。
  • 从你的Activity和Fragment中删除所有kotlin合成导入语句。
  • 在模块build.gradle文件中启用view Binding功能。
  • 在Activity和Fragment中添加绑定对象,并使用它来设置内容视图和从xml文件访问view。
  • 如果使用Parcelize注释,则将新kotlin-parcelize插件添加到模块build.gradle文件中,并如上所述更改导入语句。

译者瞎叨叨:其实早在今年3月底,JakeWharton大神就宣布了butterknife的弃用,也是推荐使用View Binding


看来绑定Android视图的方式不多了,长远看来使用官方的View BindingData Binding是比较稳妥的选择。

随着jetpack的不断完善,最近一年也看到了许多类似的变化,一些传统方式被废弃。例如Fragment的setUserVisibleHintonActivityCreated,Activity的onAciivityResult等。

jetpack中加入的新成员DataStoreHiltApp Startup等。可以看出谷歌想为开发者解决开发上的痛点,规范开发规范,加快 Android 应用开发速度。

长远看来不得不说使用jetpack还是比较放心的,不会担心无人维护。这里也吐槽一个现象,国内存在着大量的优秀开源项目,可是大多都是收割一波star,过个一两年就无人维护了,积攒着越来越多的问题,坑了不少小白。这里不乏一些上千star的热门项目。(其实不维护也没什么,至少说明一下不维护也行)

反观我们常用的okhttp rxjava glide butterknife等项目,可以说是历史悠久,但还是在不断的开发维护中,让人用着放心。

叨叨几句行了,大家都不容易,理解万岁~~

参考

  • YouTrack | Deprecate Kotlin Android Extensions compiler plugin
  • YouTrack | Move the Parcelize functionality out of the Android Extensions plugin
  • JetBrains/kotlin |Parcelize: Add integration test for the new kotlin-parcelize plugin
  • Developer Advocate for Android at Google comment on a reddit thread
  • Android Developers | Use view binding to replace findViewById

【译】迁移被废弃的Kotlin Android Extensions插件相关推荐

  1. 迁移废弃的Kotlin Android Extensions插件

    1.视图绑定 删除: apply plugin: 'kotlin-android-extensions' 使用: android {...buildFeatures {viewBinding true ...

  2. kotlin学习笔记——Kotlin Android Extensions

    Kotlin Android Extensions是另外一个团队开发的,它是一个插件所以不需要依赖别的库.当前仅仅包含view的绑定,会自动创建很多属性让我们直接访问xml中的view,我们就不需要明 ...

  3. Kotlin Android Extensions使用指南

    Kotlin Android Extensions是Kotlin团队开发的一个插件,目的是让我们在开发过程中更少的编写代码.目前包括了视图绑定的功能. 几种绑定视图方式对比** xml文件如下 < ...

  4. kotlin android 4,Kotlin Android Extensions: 与 findViewById 说再见 (KAD 04)

    时间:Aug 16, 2017 原文链接:https://antonioleiva.com/kotlin-android-extensions/ 在 Kotlin1.1.4版本 发布后,原作者依据 K ...

  5. Android KTX与Kotlin Android Extensions

    Android KTX Android KTX是Google官方推荐的一套便利的Android API扩展函数库.因还处于beta阶段,相关API并不丰富,但既然是出自JakeWharton大神之手, ...

  6. KTX 和 Kotlin android extension 都到底是个啥?

    1. KTX是什么 Android官网对于KTX的介绍:https://developer.android.com/kotlin/ktx KTX 是被称为Android之光的 JakeWharton  ...

  7. Android KTX 和 Kotlin android extension对比

    参考: KTX 和 Kotlin android extension 都到底是个啥? Android KTX KTX 是被称为Android之光的 JakeWharton 写的 Android KTX ...

  8. 迁移ADT/ANT构建的Android项目至Gradle,a walk through。

    LibModule 配置更新 9 months ago   app 配置更新 9 months ago   gradle/wrapper 配置更新 9 months ago   screenshots ...

  9. kotlin android获取按钮,Kotlin Android按钮

    Android Button是用于在单击时执行事件的按钮.它是android.widget.Button类下的一个UI组件.要了解有关Android Button的更多信息, 请参阅Android B ...

最新文章

  1. PHP脚本执行超时的解决办法
  2. 闭关修炼——two——struts2
  3. Ubuntu 12.04 一键安装lnmp环境
  4. SDK、API、JDK都是些什么?
  5. idea报错Class not found (在target中没有生成对应的class文件)
  6. 为什么一个字节定义成8位?
  7. element 怎么把数据写到日历表上_数据集 | 地理空间数据云30m的DEM爬取
  8. 学习了解online hard example mining在线难例挖掘
  9. python web环境傻瓜搭建_Python 环境搭建以及神器推荐,果断收藏!
  10. python之format函数学习
  11. 一个不错的Redis实战学习视频教程
  12. 家庭网关——开启数字家庭的钥匙
  13. Java中RGBtoHSB_HSV/HSB颜色排序列表
  14. Servlet+JSP项目
  15. 学python好就业吗?
  16. AST还原功能说明文档
  17. MySQL中like查询是否会使用索引
  18. Intellij IDEA 的 Soft-wrap 是什么
  19. java判断2的n次方_判断一个正整数是否是2的N次方的简洁算法及其证明
  20. 易语言dnf自动刷图教程

热门文章

  1. JAVA计算机毕业设计补课管理系统Mybatis+系统+数据库+调试部署
  2. Docker中的资源分配详解
  3. python 编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址_Python编程常用技巧,你知道几个?...
  4. JAVA基础语法笔记(黑马程序员系列)
  5. CNN Tensorflow 入门——以Cifar-10为例
  6. 彩灯控制单片c语言程序,基于51单片机的led彩灯设计。 9个灯(红绿蓝三色)。按键控制 c语言程序...
  7. 环信IM即时通讯的简单集成
  8. Python Tkinter 之Radiobutton控件(Python GUI 系列8)
  9. 模糊查询like 如何查找 包含% 或者_的行
  10. Source Insight 4.0 序列号 license文件