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

Reset cached bindings - #7

Open
MichaelRocks wants to merge 3 commits into
JakeWharton:masterfrom
MichaelRocks:master
Open

Reset cached bindings#7
MichaelRocks wants to merge 3 commits into
JakeWharton:masterfrom
MichaelRocks:master

Conversation

@MichaelRocks

Copy link
Copy Markdown

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

@MichaelRocks

Copy link
Copy Markdown
Author

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

@MichaelRocks

Copy link
Copy Markdown
Author

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

@MichaelRocks

Copy link
Copy Markdown
Author

Anybody alive here?

@clynamen

Copy link
Copy Markdown

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

@mopsalarm

Copy link
Copy Markdown

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

tinsukE commented Aug 8, 2017

Copy link
Copy Markdown
Contributor

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