※Notes記事では、英語のセッション動画やポッドキャストの内容を(雑に)英語でメモに書き残すことを行っています。本記事は、あくまで動画を見ながら、参考程度に読んでいただくことを想定しています。Notes記事には雑メモ程度のものだったり、書き起こしのようなものもあります。これから実際の動画を見る際には、本記事の内容が少しでもお役に立てば幸いです。(内容において不備、誤字脱字等ありましたら気軽にご連絡いただけると嬉しいです。)
本記事は、Android Jetpack sweetening Kotlin development with Android KTX (Google IO 18)の記事です。
Last Year at Google I/O
- We are allowed to extend Android APIs to have new ways of writing Android types.
- Create more concise version of what we intended to do
- Intend is to feel semantically equivalent
Why can't we directly offer APIs for it? => Lambda matters
When we pass lambda in Java 8 or in Kotlin by default it has to create anonymous class which eats a method and causes a class loading.
inline
- Language functionality which allows us to eliminate lambda's allocation
- By marking a function with
inline
, the body of the function gets copied inside the caller side, so we have zero overhead.
inline fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { for (index in 0 until childCount) { action(index, getChildAt(index)) } } val userLayout: ViewGroup = findViewById(R.id.users) userLayout.forEachIndexed ( index, view -> // Do something )
reified
- Compiler trick
- Forces the type information of the generic to be known at the compile time so that it can be made available at runtime
inline fun <reified T> Context: systemService(): = ContextCompat.getSystemService(this, T::class.kava) val notifications = systemService<NotificationManager>()
var onlyDigits = true for (c in phoneNumber) { if (!c.isDigit()) { onlyDigits = false break } }
Kotlin has a extension function isDigit()
so we can use it with all {}
:
val onlyDigits = phoneNumber.all { it.isDigit() }
But Android already has a built-in function:
val onlyDigits = TextUtils.isDigitsOnly(phoneNumber)
We can create a extension function for it:
inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this)
But, is it worth it to extract it as extension? => YES - More natural - With extension, the IDE will show this with auto-complete so you can easily find this API and you can simply press it to use it.
Android KTX
core-ktx -> support-compat -> Android framework ↓ core-ktx -> core -> Android framework fragment-ktx -> fragment palette-ktx -> palette collection-ktx -> collection lifecycle-reactivestreams-ktx -> lifecycle-reactivestreams sqlite-ktx -> sqlite navigation-*-ktx -> navigation-* work-runtime-ktx -> work-runtime
KTX Principles
✅
- Adapt existing functionality and redirect features upstream
- Default to inline unless code size or allocation is prohibitive
- If you write Kotlin by your own making
inline
as default may not be the best way as it may lead to have more overhead
- If you write Kotlin by your own making
- Leverage features unique to Kotlin
❌
- Code golf APIs to be as short as possible
- KTX is not there to make the code shorter
- Optimize for a single and/or specific use case
Reference
API reference | Android Developers
Android Developers Reference now contains some of the KTX extension functions
- Extension Function Summary
Building Kotlin-friendly Libraries
Port public API or entire library to Kotlin
- May not viable for Android Framework or androidx.* libraries
- Maybe in the future, strongly possible
Ship sibling artifact with Kotlin extensions
KEEP-110
Kotlin Evolution Enhancement Process
@ExtensionFunction / @ExtensionProperty
- Turn a static method with at least one argument into an extension function or an extension property
class TextUtils { @ExtensionFunction // Tell compiler to have an extension function for this method static boolean isDigitsOnly(CharSequence str) { int len = str.length(); // … } }
So in Kotlin side, you can use the extension function made by Kotlin compiler:
val onlyDigits = phoneNumber.isDigitsOnly() // We no longer need this extension function // inline fun CharSequence.isDigitsOnly() = TextUtils.isDigitsOnly(this) // In byte code we get val onlyDigits = TextUtils.isDigitsOnly(phoneNumber)
@KtName
- An alternative name for methods, fields, and parameters for use by Kotlin code
- Advantages
@DefaultValue
Default parameter value
class View { void setPadding( @KtName("left") @DefaultValue("paddingLeft") int left, @KtName("top") @DefaultValue("paddingTop") int top, @KtName("right") @DefaultValue("paddingRight") int right, @KtName("bottom") @DefaultValue("paddingBottom") int bottom ) { … } }
avatarView.setPadding(left = 10, right = 10) // We no longer need this extension function // inline fun View.updatePadding { … } // in byte code we get avatarView.setPadding(left = 10, top = avatarView.paddingTop, right = 10, bottom = avatarView.paddingBottom)
Note
- Semantics may change
- Naming may change
- This proposal may not be accepted and added into Kotlin compiler
- But some of the prototypes are already in Kotlin compiler
In Bug Tracker we have now new component: Android Public Tracker > App Development > Support Libraries > Android KTX
- Still accepting PRs, but issues inside GitHub is no longer the single source of truth, Bug Tracker will be.
所感・まとめ・個人的に印象に残ったことなど
- 具体的な原則をもとにどういう拡張関数を追加するかを吟味することは、Jakeとかしかできないよなぁ
- Dead code elimination(Fragment Transactionに関する拡張関数)のくだりをみると、Kotlin Compilerがどれだけ賢いかが分かる..
- KEEP-110がもし採択されたらktxいらなくなりそう、もしくはめちゃくちゃ軽量になりそう、けど、ツイッター上でどなたかおっしゃっていたとおもうけど、Javaのコード上にものすごい量のアノテーションつきそうでそれはそれでつらそう。どうなるのだろうか、どちらにしても楽しみ
- とはいえ、Androidプラットフォームに適用することにおいてはものすごく恩恵が大きそうなので個人的には実現してほしいなあという印象です..!
以上です!