So I use kotlin for android, and when inflating views, I tend to do the following:

private val recyclerView by lazy { find<RecyclerView>(R.id.recyclerView) }

这个方法会奏效的.然而,在一种情况下,它会对这款应用程序进行窃听.如果这是一个片段,并且该片段进入后栈,则将再次调用onCreateView,并且将重新创建该片段的视图层次 struct .这意味着,懒惰启动的ReceierView将指向一个不再存在的旧视图.

A solution is like this:

private lateinit var recyclerView: RecyclerView

And initialise all the properties inside onCreateView.

我的问题是,有没有办法重置惰性属性,以便可以再次初始化它们?我喜欢这样一个事实,即初始化都是在类的顶部完成的,这有助于保持代码的组织性.具体问题在这个问题中找到:kotlin android fragment empty recycler view after back

推荐答案

Here is a quick version of a resettable lazy, it could be more elegant and needs double checked for thread safety, but this is basically the idea. You need something to manage (keep track) of the lazy delegates so you can call for reset, and then things that can be managed and reset. This wraps lazy() in these management classes.

Here is what your final class will look like为例:

class Something {
    val lazyMgr = resettableManager()
    val prop1: String by resettableLazy(lazyMgr) { ... }
    val prop2: String by resettableLazy(lazyMgr) { ... }
    val prop3: String by resettableLazy(lazyMgr) { ... }
}

Then to make the lazy's all go back to new values on next time they are accessed:

lazyMgr.reset() // prop1, prop2, and prop3 all will do new lazy values on next access

The implementation of the resettable lazy:

class ResettableLazyManager {
    // we synchronize to make sure the timing of a reset() call and new inits do not collide
    val managedDelegates = LinkedList<Resettable>()

    fun register(managed: Resettable) {
        synchronized (managedDelegates) {
            managedDelegates.add(managed)
        }
    }

    fun reset() {
        synchronized (managedDelegates) {
            managedDelegates.forEach { it.reset() }
            managedDelegates.clear()
        }
    }
}

interface Resettable {
    fun reset()
}

class ResettableLazy<PROPTYPE>(val manager: ResettableLazyManager, val init: ()->PROPTYPE): Resettable {
    @Volatile var lazyHolder = makeInitBlock()

    operator fun getValue(thisRef: Any?, property: KProperty<*>): PROPTYPE {
        return lazyHolder.value
    }

    override fun reset() {
        lazyHolder = makeInitBlock()
    }

    fun makeInitBlock(): Lazy<PROPTYPE> {
        return lazy {
            manager.register(this)
            init()
        }
    }
}

fun <PROPTYPE> resettableLazy(manager: ResettableLazyManager, init: ()->PROPTYPE): ResettableLazy<PROPTYPE> {
    return ResettableLazy(manager, init)
}

fun resettableManager(): ResettableLazyManager = ResettableLazyManager()

And some unit tests to be sure:

class Tester {
   @Test fun testResetableLazy() {
       class Something {
           var seed = 1
           val lazyMgr = resettableManager()
           val x: String by resettableLazy(lazyMgr) { "x ${seed}" }
           val y: String by resettableLazy(lazyMgr) { "y ${seed}" }
           val z: String by resettableLazy(lazyMgr) { "z $x $y"}
       }

       val s = Something()
       val x1 = s.x
       val y1 = s.y
       val z1 = s.z

       assertEquals(x1, s.x)
       assertEquals(y1, s.y)
       assertEquals(z1, s.z)

       s.seed++ // without reset nothing should change

       assertTrue(x1 === s.x)
       assertTrue(y1 === s.y)
       assertTrue(z1 === s.z)

       s.lazyMgr.reset()

       s.seed++ // because of reset the values should change

       val x2 = s.x
       val y2 = s.y
       val z2 = s.z

       assertEquals(x2, s.x)
       assertEquals(y2, s.y)
       assertEquals(z2, s.z)

       assertNotEquals(x1, x2)
       assertNotEquals(y1, y2)
       assertNotEquals(z1, z2)

       s.seed++ // but without reset, nothing should change

       assertTrue(x2 === s.x)
       assertTrue(y2 === s.y)
       assertTrue(z2 === s.z)
   }
}

Kotlin相关问答推荐

在KMP中使用koin将来自Android的上下文注入到SQLDelight Driver中

Android Jetpack编写androidx.compose.oundation.lazy.grid.Items

关键字';在When Kotlin When-语句中

KMP:未能添加cafe.adriel.voyager依赖项

在 detekt 配置文件中找不到某些属性

将一个列表元素分组为多个组(indexBy)

如果带注释的成员未被特定块包围,则发出 IDE 警告

我可以在 Kotlin 中使用接口类型作为构造函数参数吗

从字符串列表构建字符串

Kotlin:查找集合中最常见的元素

Dagger 2 ContributesAndroidInjector 为模块提供活动

OnClickListener 未在 ConstraintLayout 上触发

Koin Android:org.koin.error.NoBeanDefFoundException

API 'variant.getJavaCompile()' 已过时

如何将命令行参数传递给Gradle Kotlin DSL

使用Dagger 2提供函数依赖性

Kotlin:使用自定义设置器时没有lateinit的解决方法?

如何在不绑定ViewModel(MVVM)中的UI的情况下使用android导航?

为什么在 Kotlin 中return可以返回一个return?

类型推断失败:RecyclerViewActions.scrollTo()