注册
ktx

超好用的官方core-ktx库,了解一下(终)~

Handler.postDelayed()简化lambda传入


不知道大家在使用Handler下的postDelayed()方法是不是感觉很不简洁,我们看下这个函数源码:

public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}

可以看到Runnable类型的参数r放在第一位,在Kotlin中我们就无法利用其提供的简洁的语法糖,只能这样使用:

private fun test11(handler: Handler) {
handler.postDelayed({
//编写代码逻辑
}, 100)
}

有没有感觉很别扭,估计官方也发现了这个问题,就提供了这样一个扩展方法:

public inline fun Handler.postDelayed(
delayInMillis: Long,
token: Any? = null,
crossinline action: () -> Unit
): Runnable {
val runnable = Runnable { action() }
if (token == null) {
postDelayed(runnable, delayInMillis)
} else {
HandlerCompat.postDelayed(this, runnable, token, delayInMillis)
}
return runnable
}

可以看到将函数类型(相当于上面的Runnable中的代码执行逻辑)放到了方法参数的最后一位,这样利用kotlin的语法糖就可以这样使用:

private fun test11(handler: Handler) {
handler.postDelayed(200) {

}
}

可以看到这个函数类型使用了crossinline修饰,这个是用来加强内联的,因为其另一个Runnable的函数类型中进行了调用,这样我们就无法在这个函数类型action中使用return关键字了(return@标签除外),避免使用return关键字带来返回上的歧义不稳定性


除此之外,官方core-ktx还提供了类似的扩展方法postAtTime()方法,使用和上面一样!!


Context.getSystemService()泛型实化获取系统服务


看下以往我们怎么获取ClipboardManager:

private fun test11() {
val cm: ClipboardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
}

看下官方提供的方法:

public inline fun <reified T : Any> Context.getSystemService(): T? =
ContextCompat.getSystemService(this, T::class.java)

借助于内联泛型实化简化了获取系统服务的代码逻辑:

private fun test11() {
val cm: ClipboardManager? = getSystemService()
}

泛型实化的用处有很多应用场景,大家感兴趣可以参考我另一篇文章:Gson序列化的TypeToken写起来太麻烦?优化它


Context.withStyledAttributes简化操作自定义属性


这个扩展一般只有在自定义View中较常使用,比如读取xml中设置的属性值,先看下我们平常是如何使用的:

private fun test11(
@NonNull context: Context,
@Nullable attrs: AttributeSet,
defStyleAttr: Int
) {
val ta = context.obtainStyledAttributes(
attrs, androidx.cardview.R.styleable.CardView, defStyleAttr,
androidx.cardview.R.style.CardView
)
//获取属性执行对应的操作逻辑
val tmp = ta.getColorStateList(androidx.cardview.R.styleable.CardView_cardBackgroundColor)

ta.recycle()
}

在获取完属性值后,还需要调用recycle()方法回收TypeArray,这个一旦忘记写就不好了,能让程序保证的写法那就尽量避免人为处理,所以官方提供了下面的扩展方法:

public inline fun Context.withStyledAttributes(
@StyleRes resourceId: Int,
attrs: IntArray,
block: TypedArray.() -> Unit
) {
obtainStyledAttributes(resourceId, attrs).apply(block).recycle()
}

使用如下:

private fun test11(
@NonNull context: Context,
@Nullable attrs: AttributeSet,
defStyleAttr: Int
) {
context.withStyledAttributes(
attrs, androidx.cardview.R.styleable.CardView, defStyleAttr,
androidx.cardview.R.style.CardView
) {
val tmp = getColorStateList(androidx.cardview.R.styleable.CardView_cardBackgroundColor)
}
}

上面的写法就保证了recycle()不会漏写,并且带接收者的函数类型block: TypedArray.() -> Unit也能让我们省略this直接调用TypeArray中的公共方法。


SQLiteDatabase.transaction()自动开启事务读写数据库


平常对SQLite进行写操作时为了效率及安全保证需要开启事务,一般我们都会手动进行开启和关闭,还是那句老话,能程序自动保证的事情就尽量避免手动实现,所以一般我们都会封装一个事务开启和关闭的方法,如下:

private fun writeSQL(sql: String) {
SQLiteDatabase.beginTransaction()
//执行sql写入语句
SQLiteDatabase.endTransaction()
}

官方core-ktx也提供了相似的扩展方法:

public inline fun <T> SQLiteDatabase.transaction(
exclusive: Boolean = true,
body: SQLiteDatabase.() -> T
): T {
if (exclusive) {
beginTransaction()
} else {
beginTransactionNonExclusive()
}
try {
val result = body()
setTransactionSuccessful()
return result
} finally {
endTransaction()
}
}

大家可以自行选择使用!


<K : Any, V : Any> lruCache()简化创建LruCache


LruCache一般用作数据缓存,里面使用了LRU算法来优先淘汰那些近期最少使用的数据。在Android开发中,我们可以使用其设计一个Bitmap缓存池,感兴趣的可以参考Glide内存缓存这块的源码,就利用了LruCache实现。


相比较于原有创建LruCache的方式,官方库提供了下面的扩展方法简化其创建流程:

inline fun <K : Any, V : Any> lruCache(
maxSize: Int,
crossinline sizeOf: (key: K, value: V) -> Int = { _, _ -> 1 },
@Suppress("USELESS_CAST")
crossinline create: (key: K) -> V? = { null as V? },
crossinline onEntryRemoved: (evicted: Boolean, key: K, oldValue: V, newValue: V?) -> Unit =
{ _, _, _, _ -> }
): LruCache<K, V> {
return object : LruCache<K, V>(maxSize) {
override fun sizeOf(key: K, value: V) = sizeOf(key, value)
override fun create(key: K) = create(key)
override fun entryRemoved(evicted: Boolean, key: K, oldValue: V, newValue: V?) {
onEntryRemoved(evicted, key, oldValue, newValue)
}
}
}

看下使用:

private fun createLRU() {
lruCache<String, Bitmap>(3072, sizeOf = { _, value ->
value.byteCount
}, onEntryRemoved = { evicted: Boolean, key: String, oldValue: Bitmap, newValue: Bitmap? ->
//缓存对象被移除的回调方法
})
}

可以看到,比之手动创建LruCache要稍微简单些,能稍微节省下使用成本。


bundleOf()快捷写入并创建Bundle对象


image.png


bundleOf()方法的参数被vararg声明,代表一个可变的参数类型,参数具体的类型为Pair,这个对象我们之前的文章有讲过,可以借助中缀表达式函数to完成Pair的创建:

private fun test12() {
val bundle = bundleOf("a" to "a", "b" to 10)
}

这种通过传入可变参数实现的Bundle如果大家不太喜欢,还可以考虑自行封装通用扩展函数,在函数类型即lambda中实现更加灵活的Bundle创建及写入:


1.自定义运算符重载方法set实现Bundle写入:

operator fun Bundle.set(key: String, value: Any?) {
when (value) {
null -> putString(key, null)

is Boolean -> putBoolean(key, value)
is Byte -> putByte(key, value)
is Char -> putChar(key, value)
is Double -> putDouble(key, value)
is Float -> putFloat(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Short -> putShort(key, value)

is Serializable -> putSerializable(key, value)
//其实数据类型自定参考bundleOf源码实现
}
}

2.自定义BundleBuild支持向Bundle写入多个值

class BundleBuild(private val bundle: Bundle) {

infix fun String.to(that: Any?) {
bundle[this] = that
}
}

其中to()方法使用了中缀表达式的写法


3.暴漏扩展方法实现在lambda中完成Bundle的写入和创建

private fun bundleOf(block: BundleBuild.() -> Unit): Bundle {
return Bundle().apply {
BundleBuild(this).apply(block)
}
}

然后就可以这样使用:

private fun test12() {
val bundle = bundleOf {
"a" to "haha"
//经过一些逻辑操作获取结果后在写入Bundle
val t1 = 10 * 5
val t2 = ""
t2 to t1
}
}

相比较于官方库提供的bundleOf()提供的创建方式,通过函数类型也就是lambda创建并写入Bundle的方式更加灵活,并内部支持执行操作逻辑获取结果后再进行写入。


总结


关于官方core-ktx的研究基本上已经七七八八了,总共输出了五篇相关文章,对该库了解能够节省我们编写模板代码的时间,提高开发效率,大家如果感觉写的不错,可以点个赞支持下哈,感谢!!


作者:长安皈故里
链接:https://juejin.cn/post/7124706297810780191
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册