Describe the bug
Instance of object is from wrong (previous) scope, because the scope is not closed yet.
In Android fragment lifecycle, it's possible that the new fragment will call onCreate method, while previous fragment is still executing onDestroy.
To Reproduce
To reproduce the issue let's create environment like this:
Test module:
val testFragmentModule = module {
scope(TEST_SCOPE) { (fragment: TestFragment) -> TestLogger(fragment) }
}
Test fragment:
// Test object with fragment injected via constructor
private val logger by inject<TestLogger>(parameters = { parametersOf(this) })
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bindScope(getOrCreateScope(TEST_SCOPE))
Log.d("BKZ", "Fragment: " + this.toString())
logger.test()
}
Test logger class:
class TestLogger(private val testFragment: TestFragment) {
fun test() {
Log.d("BKZ", "Logger: " + testFragment.toString())
}
}
Now let's rotate the device few times and check Logcat:
// First creation: fragment instance in the logger class is correct
D/BKZ: Fragment: TestFragment{13b19eb #3 id=0x7f08002d}
D/BKZ: Logger: TestFragment{13b19eb #3 id=0x7f08002d}
// Device rotation: Fragment has been recreated
D/BKZ: Fragment: TestFragment{a449e78 #4 id=0x7f08002d}
// TestFragment instance in the logger class is wrong, it's from the previous scope.
D/BKZ: Logger: TestFragment{13b19eb #3 id=0x7f08002d}
As you can see when bindScope(getOrCreateScope(TEST_SCOPE)) method in TestFragment was called for the second time, previous scope was not closed yet and we've received wrong instance of the fragment object, the one which was destroyed.
Expected behavior
Scope should be closed before bindScope in onCreate is called.
For now, in my project I've found workaround for this problem to close the scope when bindScope is called (I'm not sure if it will not cause any other issues):
scopedWith extension:
fun LifecycleOwner.scopedWith(scopeId: String) {
try {
getScope(scopeId).close()
} catch(e: NoScopeFoundException) { }
bindScope(createScope(scopeId))
}
in fragment:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
scopedWith(TEST_SCOPE)
}
Koin project used and used version (please complete the following information):
koin-android:1.0.0
koin-androidx-scope:1.0.0
Sure, problem is about opening/closing a scope against current lifecycle that is still closed and opening a new one on the fly 馃憤
We introduced the detach function, to detach a scope and then don't have the scope overlapping problem. This means that your are responsible of your Scope and manage it manually:
class MyScopeActivity : AppCompatActivity() {
val scope = getKoin().detachScope("session")
val presenter: MyScopePresenter by inject(scope = scope)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_simple)
bindScope(scope)
}
}
This feature is part of 1.0.2.
Most helpful comment
We introduced the
detachfunction, to detach a scope and then don't have the scope overlapping problem. This means that your are responsible of your Scope and manage it manually:This feature is part of
1.0.2.