Pass App context to Koin from Java Application class - android

I have a Java application class, I cannot change it to Kotlin. I init Koin like this:
KoinApplication koinApp = KoinApplication.create()
.printLogger()
.modules(
myModule
);
start(koinApp);
My module is created in another file:
#JvmField
val myModule = module {
single { DateUtil(androidContext()) }
factory<ListPresenterContract> { MyListPresenter() }
}
But then when I access this file DateUtil I'm getting this error regarding androidContext():
Caused by: org.koin.android.error.MissingAndroidContextException: Can't resolve Context instance. Please use androidContext() function in your KoinApplication configuration.
at org.koin.android.ext.koin.ModuleExtKt.androidContext(ModuleExt.kt:33)
at koin.MyKoinModulesKt$MyModule$1$1.invoke(MyKoinModules.kt:16)
at koin.MyKoinModulesKt$MyModule$1$1.invoke(Unknown Source:4)
at org.koin.core.instance.DefinitionInstance.create(DefinitionInstance.kt:54)
at org.koin.core.instance.SingleDefinitionInstance.get(SingleDefinitionInstance.kt:40)
at org.koin.core.definition.BeanDefinition.resolveInstance(BeanDefinition.kt:70)
at org.koin.core.scope.Scope.resolveInstance(Scope.kt:165)
at org.koin.core.scope.Scope.get(Scope.kt:128)
at view.MyViewerListAdapter$$special$$inlined$inject$1.invoke(Scope.kt:327)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at view.MyViewerListAdapter.getDateUtil(Unknown Source:7)
at view.MyViewerListAdapter.setModelView(MyListAdapter.kt:77)
at view.MyViewerListAdapter.onBindViewHolder(MyViewerListAdapter.kt:45)
at view.MyViewerListAdapter.onBindViewHolder(MyListAdapter.kt:27)
I suspect that it just so happens that my module is initialised before the application class, and gets null for the androidContext. And only when I actually access the file that supposed to have context, it doesn't lazily initialise and I still have the null context. I don't know how to get around that in a Java app class so I removed the context from the module in the meantime.

Based on the latest version 2.0.1 standard
in AppModult.kt
val appContext = module {
single(named("appContext")) { androidContext() }
}
in Application.kt
startKoin {
androidContext(this#App)
androidFileProperties()
modules(listOf(appContext))
}

Create Kotlin klass (JavaKoinApplication.kt)
fun start(myApplication: Application) {
startKoin(listOf(module)) with (myApplication)}
then Call this class in your Java Application Class
JavaKoinApplication.start(this);

Related

Koin dependency Injection Isssue - Android

In my android application am using MVVM architecture and using koin library for DI.
Below is my Repository class:
class JaiminRepository constructor(
private var remoteDataSource : RemoteDataSource,
private var enrollApiInterface : EnrollApiInterface
) {
...
}
Created module for is as below:
val jaiminRepositoryModule = module {
single {
JaiminRepository(get(),get())
}
}
For this I am getting error as :
Instance creation error : could not create instance for
[Singleton:'com.jaimin.sdk.repository.JaiminRepository']:
org.koin.core.error.NoBeanDefFoundException: |- No definition found
for class:'com.jaimin.api.RemoteDataSource'. Check your definitions!
org.koin.core.scope.Scope.throwDefinitionNotFound(Scope.kt:287)
org.koin.core.scope.Scope.resolveValue(Scope.kt:257)
So I have added factory for RemoteDataSource.
factory {
RemoteDataSource()
}
and finally it look like as below:
val jaiminRepositoryModule = module {
factory {
RemoteDataSource()
}
single {
JaiminRepository(get(),get())
}
}
But still am getting error. What might be the issue? Do I need to do something with EnrollApiInterface also? Please guide. Thanks in Advance.
You have to define all dependencies in the module(or another module), otherwise your repository can't be created. Make sure you also provide the EnrollApiInterface:
val jaiminRepositoryModule = module {
factory<EnrollApiInterface> {
EnrollApiInterfaceImpl()
}
factory {
RemoteDataSource()
}
single {
JaiminRepository(get(),get())
}
}

NoClassDefFoundError for singleton kotlin android?

I create an arr of my library module and use it in another application.
On one of the singleton class from my aar I get NoClassDefFoundError for its usage
This is my singleton class
object SharedPrefTask {
fun doSomeWork() {
/////
}
}
This is the class where I use the singleton
class ConfigController(val mContext: Context) {
private var prefTask: SharedPrefTask = SharedPrefTask
fun fetchConfig() {
val configs = prefTask.doSomeWork()
}
}
Is there something which is wrong here?
I get the exception
Fatal Exception: java.lang.NoClassDefFoundError: <clinit> failed for class m0.g; see exception in other thread
at b.a.<init>(ConfigController.kt:2)
The issue was due obsfucation from the obsfucation of proguard
Adding the rule helped resolve this
-keeppackagenames com.exaple.**

How to pass an interface as parameter in koin

I'm so beginner in koin.
I have a method that named "MesheRepoImpl" that get an interface as parameter.
I know that I can't pass an interface to a method in Koin, so I created a class and extends that from the interface then I added that class in koin module, so I use the class as parameter for MesheRepoImpl.
But android studio gives me this error:
Caused by: org.koin.core.error.NoBeanDefFoundException: |- No definition found for class:'com.app.meshe.data.repo.MesheRepo'. Check your definitions!
This is my Di module:
val mesheModule =
module {
single { getInstance(androidContext()) }
single { MesheLocalDataSource() } //*
single { MesheRepoImpl(get()) } //**
factory { TaskViewModelFactory(get()) }
viewModel { TaskViewModel(get()) }
viewModel { RewardViewModel(get()) }
viewModel {MainViewModel()}
}
The 1 star line is my class that extends from the interface and the 2 stars line is the class that get interface as parameter.
How can I pass the interface as parameter, if I can't use a class?
Since there's still no answer, I'd advise you to consider going with
interface MesheRepo
class MeshoRepoImpl(): MeshoRepo
over your
interface MesheRepo
class MeshoRepoImpl(val IRepo: MeshoRepo)
So, just implement MeshoRepo over passing it as an argument to MeshoRepoImpl.
Trying to answer directly your question, you are able to define interfaces in Koin module and pass them, but you have to provide their implementations, as well:
val mesheModule = module {
single<MeshoRepo> { MeshoRepoImpl() }
single { MeshoRepoImpl(get()) } // <-- it's like a deadlock, so I still do not see any sense to pass an interface over implementing it
}
And, please, do not forget that an interface is not an object.

Koin Android: org.koin.error.NoBeanDefFoundException

Got that message error
java.lang.RuntimeException: Unable to create application com.app.name.application.MainApplication: org.koin.error.BeanInstanceCreationException: Can't create bean Bean[class=com.app.name.general.preferences.Preferences] due to error :
org.koin.error.NoBeanDefFoundException: No definition found to resolve type 'android.app.Application'. Check your module definition
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5830)
at android.app.ActivityThread.-wrap1(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1673)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:172)
at android.app.ActivityThread.main(ActivityThread.java:6637)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: org.koin.error.BeanInstanceCreationException: Can't create bean Bean[class=com.app.name.general.preferences.Preferences] due to error :
org.koin.error.NoBeanDefFoundException: No definition found to resolve type 'android.app.Application'. Check your module definition
at org.koin.core.instance.InstanceFactory.createInstance(InstanceFactory.kt:63)
at org.koin.core.instance.InstanceFactory.retrieveInstance(InstanceFactory.kt:26)
at org.koin.KoinContext$resolveInstance$$inlined$synchronized$lambda$1.invoke(KoinContext.kt:85)
at org.koin.KoinContext$resolveInstance$$inlined$synchronized$lambda$1.invoke(KoinContext.kt:23)
at org.koin.ResolutionStack.resolve(ResolutionStack.kt:23)
at org.koin.KoinContext.resolveInstance(KoinContext.kt:80)
at com.app.name.constants.EnvironmentConstants$initEnvironmentVariables$$inlined$getKoinInstance$1$1.invoke(KoinComponent.kt:114)
at kotlin.SynchronizedLazyImpl.getValue(Lazy.kt:131)
at com.app.name.constants.EnvironmentConstants$initEnvironmentVariables$$inlined$getKoinInstance$1.getValue(Unknown Source:7)
at com.app.name.constants.EnvironmentConstants.initEnvironmentVariables(EnvironmentConstants.kt:180)
at com.app.name.application.MainApplication.onCreate(MainApplication.kt:59)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1119)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5827)
... 8 more
But all dependencies were correct.
Also I noticed that modules without androidApplication() argument works correctly.
Code looks like:
startKoin(listOf(
imageManagerModule,
databaseRepositoryModule
))
ImageManager works perfectly
val imageManagerModule: Module = applicationContext {
bean { ImageManagerImpl() as ImageManager }
}
But Preferences crashes
val preferencesModule: Module = applicationContext {
bean { PreferencesImpl(androidApplication()) as Preferences }
}
Solution is easy but not so obvious.
Somehow Android Studio imported standalone startKoin function instead of specific android function.
So I had to replace
import org.koin.standalone.StandAloneContext.startKoin
To
import org.koin.android.ext.android.startKoin
And that works!
In my case I needed to make like this:
import android.app.Application
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidFileProperties
import org.koin.android.ext.koin.androidLogger
import org.koin.android.viewmodel.dsl.viewModel
import org.koin.core.context.startKoin
import org.koin.core.module.Module
import org.koin.dsl.module
class MyApplication : Application() {
override fun onCreate(){
super.onCreate()
// start Koin!
startKoin {
// Android context
androidLogger()
androidContext(this#MyApplication)
// use the Android context given there
// load properties from assets/koin.properties file
androidFileProperties()
// modules
modules(myModule)
}
}
val myModule: Module = module { viewModel { MyViewModel() }}
}
and use older dependencies:
implementation("org.koin:koin-android:2.0.1")
implementation("org.koin:koin-android-viewmodel:2.0.1")
I had similar issue ,
Try to just add this dependency it will be resolved
// Room
implementation "android.arch.persistence.room:runtime:1.1.1"
kapt "android.arch.persistence.room:compiler:1.1.1" .
In my case for another situation it wrote this message:
"org.koin.core.error.NoBeanDefFoundException: No definition found for class:'...Actions'. Check your definitions!"
private val actions: Actions by inject()
var modules = module(override = true) {
single<Actions> { ActionsImpl(get()) }
}
#Test
fun test() {
actions.swipe()
}
In this case Actions is an interface.
Make sure you call a variable before unloadKoinModules(modules) has been called!

Dagger throw a NoSuchMethodException when creating the graph

I am using Dagger as dependency injection framwork. It's working well so far but I am having an issue while using Dagger for Android unit testing and can't figure out why (Probably because of an incorrect use of Dagger).
I am having the following exception
java.lang.IllegalArgumentException: Failed to construct com.couchsurfing.mobile.android.CSApplication$ProdModule
at dagger.internal.plugins.reflect.ReflectiveModuleAdapter.newModule(ReflectiveModuleAdapter.java:94)
at dagger.internal.RuntimeAggregatingPlugin.getModuleAdapter(RuntimeAggregatingPlugin.java:99)
at dagger.internal.RuntimeAggregatingPlugin.collectIncludedModulesRecursively(RuntimeAggregatingPlugin.java:85)
at dagger.internal.RuntimeAggregatingPlugin.getAllModuleAdapters(RuntimeAggregatingPlugin.java:71)
at dagger.ObjectGraph.makeGraph(ObjectGraph.java:115)
at dagger.ObjectGraph.create(ObjectGraph.java:103)
at com.couchsurfing.mobile.android.core.MessageManagerTest.setUp(MessageManagerTest.java:34)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:190)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:175)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1661)
Caused by: java.lang.NoSuchMethodException: <init> []
at java.lang.Class.getConstructorOrMethod(Class.java:460)
at java.lang.Class.getDeclaredConstructor(Class.java:588)
at dagger.internal.plugins.reflect.ReflectiveModuleAdapter.newModule(ReflectiveModuleAdapter.java:88)
... 15 more
The code generating the exception is the following:
public class MessageManagerTest extends InstrumentationTestCase {
#Inject
MessageManager mMessageManager;
#Inject
MessageOperations.Factory mMOFactory;
#Inject
Context mAppContext;
#Override
public void setUp() {
ObjectGraph.create(new TestModule()).inject(this);
}
#Module(
includes = CSApplication.ProdModule.class,
entryPoints = MessageManagerTest.class,
overrides = true)
static class TestModule {
#Provides
MessageOperations.Factory provideMessageOperationsFactory() {
return Mockito.mock(MessageOperations.Factory.class);
}
#Provides
Context provideAppContext() {
return Mockito.mock(Context.class);
}
}
public void testCreateMessage() throws RemoteException, OperationApplicationException {
...
}
}
Note that the module CSApplication$ProdModule is used in the production version of the app and works well.
You need to give ProdModule a no-args non-private constructor. And the class needs to be static. Without this Dagger can't construct your module.
You need to either add a no-args accessible (in this case, public) constructor or you need to pass in an instance of the module. If you don't pass in an instance, then Dagger has to construct the module itself, which it cannot do if there is no accessible no-args constructor.

Categories

Resources