2016-03-02 48 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") } 
} 

當然,你可能有你自己的緩存策略。