2016-03-02 50 views
16

于是我就用kotlin为Android和膨胀的看法时,我倾向于做到以下几点:科特林懒属性和值复位:可复位懒委托

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

这种方法会奏效。但是,有一种情况会导致应用程序出错。如果这是一个片段,并且该片段进入了后台堆栈,则onCreateView将再次被调用,并且该片段的视图层次结构将被重新创建。这意味着,惰性启动的recyclerView将指向不再存在的旧视图。

一个解决办法是这样的:

private lateinit var recyclerView: RecyclerView 

并初始化内部onCreateView所有属性。

我的问题是,有什么办法可以重置懒惰属性,以便他们可以再次初始化?我喜欢初始化都是在课程顶部完成,有助于保持代码的有序性。在这个问题中发现了具体的问题:kotlin android fragment empty recycler view after back

+0

你在寻找一个懒惰的可变变量,你可以隐式地初始化,但也可以显式设置,或者你正在寻找一个可以重新加载的加载缓存? – mfulton26

+0

我想懒惰地初始化一个属性,并根据需要重置它。在第一次初始化之前重置状态 –

+0

您需要一个自定义委托,这是相对容易编写的。如果你的场景经常被使用,这甚至可以在stdlib – voddan

回答

17

这是一个可重置的懒惰的快速版本,它可以更优雅,需要双重检查线程安全性,但这基本上是这个想法。您需要管理(跟踪)懒惰代理的内容,以便您可以调用重置,然后可以管理和重置。这在这些管理类中包装lazy()

这是你的最后一节课的样子,作为一个例子:

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

然后让懒的都回去访问它们对下一次新的价值观:

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

执行可重置的懒惰:

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() 

而一些单元测试,以确保:

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) 
    } 
} 
1

我有同样的任务,这是我用什么:

import kotlin.properties.ReadOnlyProperty 
import kotlin.reflect.KProperty 

class SingletonLazy<T : Any>(val initBlock:() -> T, val clazz: Class<T>) { 
    operator fun <R> provideDelegate(ref: R, prop: KProperty<*>): ReadOnlyProperty<R, T> = delegate() 

    @Suppress("UNCHECKED_CAST") 
    private fun <R> delegate(): ReadOnlyProperty<R, T> = object : ReadOnlyProperty<R, T> { 
     override fun getValue(thisRef: R, property: KProperty<*>): T { 
      val hash = clazz.hashCode() 
      val cached = singletonsCache[hash] 
      if (cached != null && cached.javaClass == clazz) return cached as T 
      return initBlock().apply { singletonsCache[hash] = this } 
     } 
    } 
} 

private val singletonsCache = HashMap<Int, Any>() 

fun <T> clearSingleton(clazz: Class<T>) : Boolean { 
    val hash = clazz.hashCode() 
    val result = singletonsCache[hash] 
    if (result?.javaClass != clazz) return false 

    singletonsCache.remove(hash) 
    return true 
} 

inline fun <reified T : Any> singletonLazy(noinline block:() -> T): SingletonLazy<T> 
     = SingletonLazy(block, T::class.java) 

用法:

val cat: Cat by singletonLazy { Cat() } 

fun main(args: Array<String>) { 
    cat 
    println(clearSingleton(Cat::class.java)) 
    cat // cat will be created one more time 
    println(singletonsCache.size) 
} 

class Cat { 
    init { println("creating cat") } 
} 

当然,你可能有你自己的缓存策略。