译迁移被废弃的Kotlin Android Extensions插件
Posted 唯鹿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了译迁移被废弃的Kotlin Android Extensions插件相关的知识,希望对你有一定的参考价值。
原文: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: ActivityMainBinding
override 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.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:context=".MainActivity">
<TextView
android: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
@NonNull
private final ConstraintLayout rootView;
@NonNull
public 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
插件 。
- 选择Tools→ Kotlin →Configure Kotlin Plugin Updates
- 在Update channel 列表中,选择Early Access Preview 1.4.x频道
- 点击Check again.
- 然后点击Install
太长不看
这是从kotlin-android-extensions
插件迁移到ViewBinding
和kotlin-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 Binding
和Data Binding
是比较稳妥的选择。
随着jetpack的不断完善,最近一年也看到了许多类似的变化,一些传统方式被废弃。例如Fragment的setUserVisibleHint
、 onActivityCreated
,Activity的onAciivityResult
等。
jetpack中加入的新成员DataStore
、Hilt
、App 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插件的主要内容,如果未能解决你的问题,请参考以下文章
Android 开发技术——从 LiveData 迁移到 Kotlin 数据流
一年时间过去了,LiveData真的被Flow代替了吗? LiveData会被废弃吗?
一年时间过去了,LiveData真的被Flow代替了吗? LiveData会被废弃吗?