遷移被廢棄的Kotlin Android Extensions插件!
Android加入安卓技術(shù)群
原文:Migrating the deprecated Kotlin Android Extensions compiler plugin
作者:Ahmad El-Melegy 譯者:唯鹿
在Kotlin 1.4.20-M2中,JetBrains廢棄了Kotlin Android Extensions編譯插件。
其實(shí)這是早就預(yù)料到的,你可以在這次提交中看到詳情。
kotlinx.android.synthetic不再是一個(gè)推薦的做法。刪除了顯式的findViewById。
但為什么呢?
kotlinx的合成屬性存在一些眾所周知的問(wèn)題。
-
它公開(kāi)了以view的id為名的全局變量,但該名稱(chēng)與實(shí)際的布局無(wú)關(guān),沒(méi)有針對(duì)無(wú)效查找進(jìn)行檢查。
-
它只適用于Kotlin。
-
當(dāng)View只存在于某些配置中時(shí),它們沒(méi)有空安全提示。
-
所有這些問(wèn)題加在一起,導(dǎo)致增加了Android應(yīng)用的崩潰次數(shù)。
-
另外谷歌正在推廣模塊化,但合成屬性不能跨模塊工作。這是自2018年1月以來(lái)的一個(gè)公開(kāi)問(wèn)題。
有哪些替代方案?
-
View Binding是視圖查找以及綁定的推薦方案,但與Android Kotlin Extensions相比,它確實(shí)增加了一些開(kāi)銷(xiāo)。但它增加了編譯時(shí)對(duì)視圖查找的檢查和類(lèi)型安全。 -
傳統(tǒng)方式
findViewById,Kotlin和Java都適用。
JetBrains廢棄了Kotlin Android Extensions,推薦使用View Binding,所以我們將在本文中探討如何遷移到View Binding。
View Binding
不要與Data Binding混淆
View Binding是一種功能,它允許您更容易地編寫(xiě)與視圖交互的代碼。
一旦在一個(gè)模塊中啟用了View Binding,它就會(huì)為該模塊中存在的每個(gè) XML 布局文件生成一個(gè)綁定類(lèi)。
-
綁定類(lèi)的實(shí)例包含對(duì)相應(yīng)布局中具有ID的所有VIew的直接引用。
-
View Binding對(duì)于在多個(gè)配置中定義的布局來(lái)說(shuō)是Null-safe的。
-
View Binding將檢測(cè)視圖是否只存在于某些配置中,并創(chuàng)建一個(gè)@Nullable屬性。
-
View Binding適用于Java和Kotlin。
如何啟用View Binding?
你不需要添加任何額外的庫(kù)來(lái)啟用View Binding。從Android Studio 3.6版本開(kāi)始,它就被內(nèi)置到Android Gradle Plugin中了。如果要在模塊中啟用該功能,請(qǐng)?jiān)谀愕?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">build.gradle文件中添加以下內(nèi)容。
android {
...
buildFeatures {
viewBinding true
}
}
如何使用View Binding?
如果為模塊啟用了View Binding,則會(huì)為模塊包含的每個(gè) XML 布局文件生成一個(gè)綁定類(lèi)。
每個(gè)綁定類(lèi)都包含對(duì)根視圖和所有具有ID的視圖的引用。
綁定類(lèi)的名稱(chēng)是通過(guò)將 XML 文件的名稱(chēng)轉(zhuǎn)換為駝峰式大小寫(xiě),并在結(jié)尾處添加 Binding 一詞來(lái)生成的。
譯者注: 例如,假設(shè)某個(gè)布局文件的名稱(chēng)為 result_profile.xml,所生成的綁定類(lèi)的名稱(chēng)就為 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對(duì)象訪問(wèn)視圖:
binding.name.text = "Some Text"
在Fragment中使用View Binding
在Fragment中使用View Binding需要多加注意,如果使用不當(dāng)它會(huì)引發(fā)內(nèi)存泄漏,如果你沒(méi)有在onDestroy中將view置空,那么它就不會(huì)從內(nèi)存中清除。
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
}
然后就可以像我們?cè)贏ctivity中那樣使用它。
binding.name.text = "Some Text"
原理
View Binding將為模塊中的每個(gè)XML布局生成一個(gè)綁定對(duì)象。
例如這個(gè)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將為每個(gè)具有id的視圖生成一個(gè)正確類(lèi)型的屬性。它還會(huì)生成一個(gè)名為rootView的屬性。
視圖綁定對(duì)Kotlin是友好的,因?yàn)樗械膶傩远急蛔⒔鉃?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">@Nullable或@NonNull,Kotlin知道如何將它們暴露為空安全類(lèi)型。
@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中,視圖綁定會(huì)生成一個(gè)公共的inflate方法。
它調(diào)用bind,在那里它將獲取布局并綁定屬性,并進(jìn)行一些錯(cuò)誤檢查。
@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方法中,生成的綁定對(duì)象將為每個(gè)要綁定的View調(diào)用findViewById。
那么Kotlin Android Extensions的Parcelize特性呢?
不要忘了,Kotlin中的Parcelize功能是kotlin-android-extensions編譯器插件的一部分,所以如果你的Parcelable類(lèi)依賴(lài)于Parcelize注解,那么移除該插件將使它們無(wú)法編譯。
JetBrains將Kotlin Android Extensions中的Parcelize提取到一個(gè)新的插件kotlin-parcelize中。
首先你需要在你的模塊中添加kotlin-parcelize插件。
plugins {
..
id 'kotlin-parcelize'
}
然后更改舊的import語(yǔ)句,將:
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
你可能會(huì)發(fā)現(xiàn)這個(gè)IDE錯(cuò)誤
Class ‘User’ is not abstract and does not implement abstract member public abstract fun describeContents(): Int defined in android.os.Parcelable
別擔(dān)心,你的應(yīng)用會(huì)很好地構(gòu)建,這是一個(gè)誤報(bào)的錯(cuò)誤,已經(jīng)在youtrack上被報(bào)告,并被修復(fù),所以應(yīng)該在下一個(gè)版本中發(fā)布。
請(qǐng)注意,這個(gè)插件只能1.4.20-M2版本開(kāi)始使用,這個(gè)版本也是廢棄kotlin-android-extensions編譯器插件的版本。
如果你想嘗試一下,你需要在IntelliJ IDEA或Android Studio安裝Kotlin EAP 插件 。
-
選擇Tools→ Kotlin →Configure Kotlin Plugin Updates -
在Update channel 列表中,選擇Early Access Preview 1.4.x頻道 -
點(diǎn)擊Check again. -
然后點(diǎn)擊Install
太長(zhǎng)不看
這是從kotlin-android-extensions插件遷移到ViewBinding和kotlin-parcelize插件時(shí)應(yīng)該做的事情:
-
將kotlin-android-extensions插件從build.gradle文件中刪除。 -
從你的Activity和Fragment中刪除所有kotlin合成導(dǎo)入語(yǔ)句。 -
在模塊build.gradle文件中啟用view Binding功能。 -
在Activity和Fragment中添加綁定對(duì)象,并使用它來(lái)設(shè)置內(nèi)容視圖和從xml文件訪問(wèn)view。 -
如果使用Parcelize注釋?zhuān)瑒t將新kotlin-parcelize插件添加到模塊build.gradle文件中,并如上所述更改導(dǎo)入語(yǔ)句。
譯者瞎叨叨:其實(shí)早在今年3月底,JakeWharton大神就宣布了butterknife的棄用,也是推薦使用View Binding。
看來(lái)綁定Android視圖的方式不多了,長(zhǎng)遠(yuǎn)看來(lái)使用官方的View Binding和Data Binding是比較穩(wěn)妥的選擇。
隨著jetpack的不斷完善,最近一年也看到了許多類(lèi)似的變化,一些傳統(tǒng)方式被廢棄。例如Fragment的setUserVisibleHint 、 onActivityCreated,Activity的onAciivityResult等。
jetpack中加入的新成員DataStore 、Hilt 、App Startup等。可以看出谷歌想為開(kāi)發(fā)者解決開(kāi)發(fā)上的痛點(diǎn),規(guī)范開(kāi)發(fā)規(guī)范,加快 Android 應(yīng)用開(kāi)發(fā)速度。
參考
-
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
·················END················· 推薦閱讀
? 耗時(shí)2年,Android進(jìn)階三部曲第三部《Android進(jìn)階指北》出版!
? 『BATcoder』做了多年安卓還沒(méi)編譯過(guò)源碼?一個(gè)視頻帶你玩轉(zhuǎn)!
BATcoder技術(shù)群,讓一部分人先進(jìn)大廠
你好,我是劉望舒,騰訊云最具價(jià)值專(zhuān)家TVP,著有暢銷(xiāo)書(shū)《Android進(jìn)階之光》《Android進(jìn)階解密》《Android進(jìn)階指北》,蟬聯(lián)四屆電子工業(yè)出版社年度優(yōu)秀作者,谷歌開(kāi)發(fā)者社區(qū)特邀講師。
前華為面試官,現(xiàn)大廠技術(shù)負(fù)責(zé)人。
歡迎添加我的微信 henglimogan ,備注:BATcoder,加入BATcoder技術(shù)群。
為了防止失聯(lián),歡迎關(guān)注我的小號(hào)
更文不易,點(diǎn)個(gè)“在看”支持一下??
