Skip to content
This repository has been archived by the owner on Sep 12, 2019. It is now read-only.

Reset cached bindings #7

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

Reset cached bindings #7

wants to merge 3 commits into from

Conversation

MichaelRocks
Copy link

Fixes #5 by keeping a registry of cached lazies and resetting them on purpose.

@MichaelRocks
Copy link
Author

Seems this patch causes memory leaks so it shouldn't be merged. I'll try to find a better solution.

@MichaelRocks
Copy link
Author

The memory leak is fixed now. @JakeWharton, can you merge these commits please?

@MichaelRocks
Copy link
Author

Anybody alive here?

@clynamen
Copy link

without this change fragments cannot be used when the views are destroyed (e..g with page views )

@mopsalarm
Copy link

I support this. I would like to see this merged too.

@Suppress("UNCHECKED_CAST")
private fun <T : Any, V : View> required(id: Int, finder: T.(Int) -> View?)
        = Lazy { t: T, desc -> t.finder(id) as V? ?: viewNotFound(id, desc) }

@Suppress("UNCHECKED_CAST")
private fun <T : Any, V : View> optional(id: Int, finder: T.(Int) -> View?)
        = Lazy { t: T, desc -> t.finder(id) as V? }

@Suppress("UNCHECKED_CAST")
private fun <T : Any, V : View> required(ids: IntArray, finder: T.(Int) -> View?)
        = Lazy { t: T, desc -> ids.map { t.finder(it) as V? ?: viewNotFound(it, desc) } }

@Suppress("UNCHECKED_CAST")
private fun <T : Any, V : View> optional(ids: IntArray, finder: T.(Int) -> View?)
        = Lazy { t: T, desc -> ids.map { t.finder(it) as V? }.filterNotNull() }

// Like Kotlin's lazy delegate but the initializer gets the target and metadata passed to it
private class Lazy<T : Any, V>(private val initializer: (T, KProperty<*>) -> V) : ReadOnlyProperty<T, V> {
    private object EMPTY

    private var value: Any? = EMPTY

    override fun getValue(thisRef: T, property: KProperty<*>): V {
        if (value == EMPTY) {
            value = initializer(thisRef, property)
            LazyRegistry.register(thisRef, this)
        }
        @Suppress("UNCHECKED_CAST")
        return value as V
    }

    internal fun reset() {
        value = EMPTY
    }
}

private object LazyRegistry {
    private val lazyMap = WeakHashMap<Any, MutableCollection<Lazy<*, *>>>()

    fun register(target: Any, lazy: Lazy<*, *>) {
        lazyMap.getOrPut(target, { Collections.newSetFromMap(WeakHashMap()) }).add(lazy)
    }

    fun reset(target: Any) {
        lazyMap.get(target)?.forEach { it.reset() }
    }
}

object ButterKnife {
    fun reset(target: Any) {
        LazyRegistry.reset(target)
    }
}

@tinsukE
Copy link
Contributor

tinsukE commented Aug 8, 2017

We need this functionality to correctly implement Fragments being detached and re-attached (ex: ViewPager).

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bind view in retained Fragment
4 participants