Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fragment scope problem of reuse #1092

Closed
ZuiRenA opened this issue May 13, 2021 · 2 comments
Closed

Fragment scope problem of reuse #1092

ZuiRenA opened this issue May 13, 2021 · 2 comments

Comments

@ZuiRenA
Copy link
Contributor

ZuiRenA commented May 13, 2021

Describe the bug
When I use BottomNavigationView, two Fragments are used to display the page, and their ViewBinding both use Koin inject, but from Fragment A to Fragment B, and then from Fragment B to Fragment A, it throw can' t get Scope for lifecycleOwner

My Code

class MainActivity : BaseScopeActivity() {
....
   private val homeFragment: HomeFragment by inject()
   private val mineFragment: MineFragment by inject()

   fun  replaceCurrentPage(type: Int) {
       val targetFragment = if (type == 0) homeFragment else mineFragment
       supportFragmentManager.beginTransaction()
            .replace(R.id.mainContainer, targetFragment)
            .commit()
   }
....
}
class MineFragment : BaseScopeFragment<FragmentMineBinding>() {

    override fun initBinding(
        inflater: LayoutInflater,
        container: ViewGroup?
    ): Lazy<FragmentMineBinding> = inject { parametersOf(inflater, container) }
}

solution
When I debug fragmentScope, I found that _scope of LifecycleScopeDelegate will be set to null when the fragment is replaced. But because the _scope of LifecycleScopeDelegate will not be reassigned, an exception is thrown.

At first, I tried this, but I found that getValue is called faster than onCreate

@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onCreate(owner: LifecycleOwner) {
     if (_scope != null && !_scope!!.closed) {
          return
     }
     val internalScopeId = owner.getScopeId()
     _scope = koin.getScopeOrNull(internalScopeId) ?: createScope(koin)
     logger.debug("Recreate scope: $_scope for $owner")
}

Then I can only use this way

    override fun getValue(thisRef: LifecycleOwner, property: KProperty<*>): Scope {
//        return _scope ?: error("can't get Scope for $lifecycleOwner")
        if (_scope != null) return _scope!!

        val ownerState = thisRef.lifecycle.currentState
        val ownerIsActive =
            ownerState.isAtLeast(Lifecycle.State.CREATED)
        if (!ownerIsActive) {
            error("can't get Scope for $lifecycleOwner")
        }
        
        val koin = koinContext.get()
        _scope = koin.getScopeOrNull(thisRef.getScopeId()) ?: createScope(koin)
        return _scope!!
    }

Koin project used and used version
koin-android version 3.0.1
koin-android-ext version 3.0.1

Summary
I think this is because Koin has a problem with the lifecycle design of Fragment. Do you have any better solutions?

@hungyanbin
Copy link

I write my custom extension function as a workaround, but I think this is not a good solution

fun Fragment.activityScope(): Lazy<Scope> {
    return lazy {
        activity?.getScopeOrNull()
            ?: throw IllegalStateException("Can not find activity scope for ${this::class.java.simpleName}")
    }
}

@arnaudgiuliani
Copy link
Member

Will be fix in PR

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

No branches or pull requests

3 participants