Closed arnaudgiuliani closed 5 years ago
// don't forget to start it
why do I have to start it in a separate step? Or phrased another way: when would one not want to start it?
DSL style:
al app2 = koinApplication {
loadModules(
module {
You are mixing imperative and declarative styles. It think DSLs read nicest when they stick to declarative. i.e. loadModules
and useLogger
feel out of place.
Compare to:
koinApplication {
// Use Android Logger
androidLogger(Level.DEBUG)
// Use Android Context from MainApplication
androidContext(this@MainApplication)
// Load modules
modules(myModule)
}
why do I have to start it in a separate step? Or phrased another way: when would one not want to start it?
start()
on a KoinApplication
register it in the StandAloneContext and allow default use of Koin (by inject() ...).
A better naming with that also? register()
or toStandalone()
?
it think DSLs read nicest when they stick to declarative
ok, yep :)
Hi! I updated my performance test project with the new alpha: https://github.com/Sloy/android-dependency-injection-performance/compare/koin2
Here are some results in a low end device: Samsung j5nlte with Android 6.0.1
The improvement is huge. There might be room for improvement still, but at least it feels production-ready in terms of performance. Also, there's still that strange impact when loading Java classes. I'm not sure where's that coming from.
Test: | Koin 1.0.1 + Kotlin |
---|---|
Startup: | 1121,12 ms |
Min-Max: | 268,89-290,61 ms |
Average: | 271,42 ms |
Test: | Koin 1.0.1 + Java |
---|---|
Startup: | 1578,65 ms |
Min-Max: | 404,19-416,20 ms |
Average: | 406,65 ms |
Test: | Koin 2.0.0 + Kotlin |
---|---|
Startup: | 709,51 ms |
Min-Max: | 11,80-19,02 ms |
Average: | 12,41 ms |
Test: | Koin 2.0.0 + Java |
---|---|
Startup: | 1423,47 ms |
Min-Max: | 11,97-16,32 ms |
Average: | 12,48 ms |
Test: | Kodein + Kotlin |
---|---|
Startup: | 535,60 ms |
Min-Max: | 8,24-16,70 ms |
Average: | 8,60 ms |
Test: | Kodein + Java |
---|---|
Startup: | 506,22 ms |
Min-Max: | 8,78-11,80 ms |
Average: | 9,11 ms |
Test: | Dagger2 + Kotlin |
---|---|
Startup: | 41,53 ms |
Min-Max: | 0,24-6,26 ms |
Average: | 0,31 ms |
Test: | Dagger2 + Java |
---|---|
Startup: | 41,21 ms |
Min-Max: | 0,21-6,02 ms |
Average: | 0,28 ms |
Test: | Custom + Kotlin |
---|---|
Startup: | 408,04 ms |
Min-Max: | 0,68-0,82 ms |
Average: | 0,70 ms |
Test: | Custom + Java |
---|---|
Startup: | 405,69 ms |
Min-Max: | 0,82-1,05 ms |
Average: | 0,84 ms |
Cool. Thanks for your benchmark.
Hi, here is a new perfs improvments with 2.0.0-alpha-3
Here some new perfs, done with the sloy app on my LG G4 (helped me fixed some perfs again):
I/DI-TEST: **Custom** | 3,18 ms | 3,38 ms | 0,52 ms | 0,61 ms
I/DI-TEST: **Katana** | 8,83 ms | 8,34 ms | 1,57 ms | 1,46 ms
I/DI-TEST: **Koin** | 40,55 ms | 40,08 ms | 1,81 ms | 1,80 ms
I/DI-TEST: **Kodein** | 57,77 ms | 59,23 ms | 6,14 ms | 6,06 ms
I/DI-TEST: **Dagger** | 0,01 ms | 0,01 ms | 0,18 ms | 0,15 ms
(seems that Dagger's resolution is already done - should be at least need the time to create all graph object) @Sloy I have some suggestions for your test: https://github.com/arnaudgiuliani/android-dependency-injection-performance/commit/a4571d3e9d97bc2d05bc8f02df387fbf19fef228
--
Starting DSL is now like more declarative:
fun main(vararg args: String) {
startKoin {
logger()
modules(coffeeAppModule)
}
}
I've have a now introduced the GlobalContext
concept to replace the old StandAloneContext
which tells more what is it clearly now: a global context resolution.
Let's add the Androidx Fragment factory next!
I need to make some doc now :)
@arnaudgiuliani thanks for the tip! I just changed the code an the stats. Nice improvement on alpha-3 👍 👏
So far so good. Tested on an app woth (so far) ~350 definitions. Minor changes were required only.
I've tried 2.0.0 and seems that there is something broken with the scopes.
I have a BaseFragment
that does bindScope(getOrCreateScope(SCOPE_FRAGMENT))
.
And have a fragment stack (extending BaseFragment) of fragments A->B
.
With Koin 2.0.0, when I keep popping the stack by pressing back to return to launcher it crashes with this exception.
java.lang.RuntimeException: Unable to destroy activity {de.r4md4c.gamedealz/de.r4md4c.gamedealz.home.HomeActivity}: java.lang.RuntimeException: Failed to call observer method
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4458)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4476)
at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:39)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:145)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.RuntimeException: Failed to call observer method
at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:225)
at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:193)
at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:184)
at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:36)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:355)
at androidx.lifecycle.LifecycleRegistry.backwardPass(LifecycleRegistry.java:309)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:328)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:138)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:124)
at androidx.fragment.app.Fragment.performDestroy(Fragment.java:2693)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1016)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1229)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1295)
at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2605)
at androidx.fragment.app.FragmentManagerImpl.dispatchDestroy(FragmentManagerImpl.java:2596)
at androidx.fragment.app.Fragment.performDestroy(Fragment.java:2695)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1016)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1229)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1295)
at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2605)
at androidx.fragment.app.FragmentManagerImpl.dispatchDestroy(FragmentManagerImpl.java:2596)
at androidx.fragment.app.FragmentController.dispatchDestroy(FragmentController.java:318)
at androidx.fragment.app.FragmentActivity.onDestroy(FragmentActivity.java:354)
at androidx.appcompat.app.AppCompatActivity.onDestroy(AppCompatActivity.java:211)
at android.app.Activity.performDestroy(Activity.java:7395)
at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1306)
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:4443)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:4476)
at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:39)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:145)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.IllegalStateException: Scope not found 'd023d781-7f16-4358-ac91-13d8ac5e3327'
at org.koin.core.Koin.closeScope$koin_core(Koin.kt:182)
at org.koin.core.scope.Scope.close(Scope.kt:41)
at org.koin.androidx.scope.ScopeObserver.onDestroy(ScopeObserver.kt:54)
I believe that is because Koin has already closed this scope when fragment B
was popped.
With Koin 1.0.2
this crash doesn't happen.
Scope API is still in progress 👍
For Java, can there be @Inject annotation support instead of needing to do this?
private Lazy
no annotation processing in Koin's target for now.
One of the reasons I use Koin is to avoid annotation processing. Will it be optional in the planned future releases?
There is nothing to be made optional, because there is no annotation processing at all. And indeed there should not be (imho). Any such thing could, if one wants it, be made as a seperate library.
For some reaason the koinApplication
api doesn't work for me. I get this compilation error:
koinApplication { modules(coreModules + libModules + featureModules) }.checkModules()
None of the following functions can be called with the arguments supplied.
I am using the latest Koin version 2.0.0-rc-1 and have a simple test case in my androidTest folder.
class UserTestCase : KoinTest
{
@Before
fun before()
{
startKoin {
modules(testModule1)
}
}
@After
fun after()
{
stopKoin()
}
@Test
fun test()
{
}
}
val testModule1= module {
single{ User(1,"Random","Random",false)}
}
I get "A Koin Application has already been started". Is this because my main application class also has a startKoin over there?.
in our tests, I always call stopKoin before startKoin...don't do it in the @after
I get "A Koin Application has already been started". Is this because my main application class also has a startKoin over there?.
Yes, it is the most relevant reason in case you use instrumented tests (they are executed on android device and thus android application class is instantiated => it causes Koin initialization as a side effect.
In unit tests you should initialize Koin by yourself.
No tests found for given includes: HelloAppTest when I want to run sample test. Can you help please
`@ExperimentalCoroutinesApi @InternalCoroutinesApi class OmdbServicesTest : AutoCloseKoinTest() {
@get:Rule
var rule = InstantTaskExecutorRule()
@get:Rule
var mockWebServer = MockWebServerRule()
private val mockedApplication = mock(OmdbApplication::class.java)
private val service: OMDBRemoteServices = get()
@Before
fun before() {
val serviceModule by lazy {
module {
fun provideGson(): Gson =
GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY).create()
fun provideOkHttpClient(): OkHttpClient =
OkHttpClient.Builder()
.build()
fun provideOmdbService(): OMDBRemoteServices =
Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(get()))
.client(get())
.build()
.create(OMDBRemoteServices::class.java)
single { provideGson() }
single { provideOkHttpClient() }
single { provideOmdbService() }
}
}
stopKoin()
startKoin {
androidContext(mockedApplication)
modules(serviceModule)
}
}
@Test
fun omdbListTest() {
val execute = service.getOmdbSearchData(
BuildConfig.API_KEY,
"friend",
1
).execute()
assertTrue(execute.isSuccessful && execute.body() != null)
}
}`
I am getting the below while executing the above test class
`java.lang.IllegalStateException: No Koin Context configured. Please use startKoin or koinApplication DSL.
at org.koin.core.context.KoinContextHandler.getContext(KoinContextHandler.kt:29)
at org.koin.core.context.KoinContextHandler.get(KoinContextHandler.kt:35)
at org.koin.core.KoinComponent$DefaultImpls.getKoin(KoinComponent.kt:32)
at org.koin.test.KoinTest$DefaultImpls.getKoin(KoinTest.kt)
at org.koin.test.AutoCloseKoinTest.getKoin(AutoCloseKoinTest.kt:26)
at com.myomdbapplication.service.OmdbServicesTest.<init>(OmdbServicesTest.kt:86)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:250)
at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:260)
at org.junit.runners.BlockJUnit4ClassRunner$2.runReflectiveCall(BlockJUnit4ClassRunner.java:309)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)`
java.lang.IllegalStateException: No Koin Context configured. Please use startKoin or koinApplication DSL.
`@ExperimentalCoroutinesApi @InternalCoroutinesApi class OmdbServicesTest : AutoCloseKoinTest() {
@get:Rule var rule = InstantTaskExecutorRule() @get:Rule var mockWebServer = MockWebServerRule() private val mockedApplication = mock(OmdbApplication::class.java) private val service: OMDBRemoteServices = get() @Before fun before() { val serviceModule by lazy { module { fun provideGson(): Gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.IDENTITY).create() fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder() .build() fun provideOmdbService(): OMDBRemoteServices = Retrofit.Builder() .baseUrl(BuildConfig.BASE_URL) .addConverterFactory(GsonConverterFactory.create(get())) .client(get()) .build() .create(OMDBRemoteServices::class.java) single { provideGson() } single { provideOkHttpClient() } single { provideOmdbService() } } } stopKoin() startKoin { androidContext(mockedApplication) modules(serviceModule) } } @Test fun omdbListTest() { val execute = service.getOmdbSearchData( BuildConfig.API_KEY, "friend", 1 ).execute() assertTrue(execute.isSuccessful && execute.body() != null) }
}`
I am getting the below while executing the above test class
`java.lang.IllegalStateException: No Koin Context configured. Please use startKoin or koinApplication DSL.
at org.koin.core.context.KoinContextHandler.getContext(KoinContextHandler.kt:29) at org.koin.core.context.KoinContextHandler.get(KoinContextHandler.kt:35) at org.koin.core.KoinComponent$DefaultImpls.getKoin(KoinComponent.kt:32) at org.koin.test.KoinTest$DefaultImpls.getKoin(KoinTest.kt) at org.koin.test.AutoCloseKoinTest.getKoin(AutoCloseKoinTest.kt:26) at com.myomdbapplication.service.OmdbServicesTest.<init>(OmdbServicesTest.kt:86) at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:423) at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:250) at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:260) at org.junit.runners.BlockJUnit4ClassRunner$2.runReflectiveCall(BlockJUnit4ClassRunner.java:309) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:306) at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63) at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.junit.runners.ParentRunner.run(ParentRunner.java:413) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)`
try checking that have you registered the Application in manifest file and use androidLogger(Level.ERROR) for logging it worked for me, I was getting the same error
Hello all :)
Koin
2.0.0-alpha-2
has been published in jcenter 👍 Here is how you can test it.Same API But changes import: inject(), get(), getViewModel(), viewModel() ... Also note that you can request by KClass with get() (check function signature)
Scope group declaration
checkModules()
is available on a KoinApplicationKoin from Java:
Koin extensions still available:
Feel free to give your feedback!
Cheers.