2016-03-02 7 views
16

per cui uso kotlin per Android, e quando il gonfiaggio di vista, io tendo a fare quanto segue:Kotlin proprietà pigri e valori di reset: a azzerabile delegato pigro

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

Questo metodo funziona. Tuttavia, c'è un caso in cui disturberà l'app. Se si tratta di un frammento e il frammento viene spostato nel backstack, verrà richiamato onCreateView e verrà ricreata la gerarchia della vista del frammento. Il che significa che il riciclatore avviato pigro indicherà una vecchia visualizzazione non più esistente.

una soluzione è simile a questo:

private lateinit var recyclerView: RecyclerView 

E inizializzare tutte le proprietà all'interno onCreateView.

La mia domanda è, c'è un modo per ripristinare le proprietà pigro in modo che possano essere nuovamente inizializzati? Mi piace il fatto che le inizializzazioni siano tutte fatte all'inizio di una classe, aiuta a mantenere il codice organizzato. Il problema specifico si trova in questa domanda: kotlin android fragment empty recycler view after back

+0

Stai cercando una variabile mutabile pigra che puoi inizializzare implicitamente ma puoi anche impostare esplicitamente o stai cercando una cache di caricamento che puoi ricaricare? – mfulton26

+0

Voglio inizializzare pigramente una proprietà con l'opzione per resettarla se necessario. Se si ripristina lo stato precedente alla prima inizializzazione –

+0

, è necessario un delegato personalizzato per questo, è relativamente facile da scrivere. Se lo scenario è spesso utilizzato, questo può essere implementato anche in stdlib – voddan

risposta

17

Ecco una versione rapida di un pigro azzerabile, potrebbe essere più elegante e necessita di un doppio controllo per la sicurezza del thread, ma questa è fondamentalmente l'idea. Hai bisogno di qualcosa per gestire (tenere traccia) dei delegati pigri in modo da poter chiamare per il ripristino, e quindi le cose che possono essere gestite e ripristinate. Questo include lazy() in queste classi di gestione.

Ecco quello che la classe finale sarà simile, come ad esempio:

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

poi a fare i pigri è tutto tornare a New valori sulla prossima volta vi si accede:

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

l'attuazione del pigro ripristinabile:

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

E alcuni test di unità per essere sicuri:

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

Ho avuto lo stesso compito, e questo è ciò che ho usato:

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) 

utilizzo:

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

Ovviamente, potresti avere le tue strategie di memorizzazione nella cache.

Problemi correlati