I'm maintaining a large app (mostly) using a "one feature one activity"-architecture.
Now i'd like to scope a usecase, so it lives as long as the activity, something like this:
// koin module
scope<MyFeatureActivity> {
viewModel { MyFeatureActivityViewModel() }
viewModel { MyFeatureFragmentAViewModel(usecase = get()) }
viewModel { MyFeatureFragmentBViewModel(usecase = get()) }
scoped { MyFeatureUseCase() }
}
// fragments
class FeatureAFragment: AppCompatDialogFragment(){
private val viewModel by viewModel<MyFeatureFragmentAViewModel>()
....
}
// activity
class MyFeatureActivity : ScopeActivity() { ... }
However, this doesn't work. When launching MyFeatureFragmentA from MyFeatureActivity it's throwing an Exception:
org.koin.core.error.NoBeanDefFoundException:
|- No definition found for class:'MyFeatureAViewModel'. Check your definitions!
What am i doing wrong?
Please note: I would not like to just skip scopes and make the usecase a single (or a factory), since it actually stores some data relevant to only this activity: The data should be kept while we're in this feature, but dismissed when leaving it.
Related
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.
I have a repository where a chain of network requests is calling. The repository is accessed from the interactor. And interactor is accessed from viewModel. The view model is attached to activity A. If I go to activity B, which has its own viewModel, then the request chain in the repository of activity A does not complete its execution.
Is it possible to make a repository whose life cycle will be equal to the life cycle of the application. I need all requests to complete even if I go to a new activity.
Please, help me.
This is covered in the Coroutines guide on developer.android.com
class ArticlesRepository(
private val articlesDataSource: ArticlesDataSource,
private val externalScope: CoroutineScope,
private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
) {
// As we want to complete bookmarking the article even if the user moves
// away from the screen, the work is done creating a new coroutine
// from an external scope
suspend fun bookmarkArticle(article: Article) {
externalScope.launch(defaultDispatcher) {
articlesDataSource.bookmarkArticle(article)
}
.join() // Wait for the coroutine to complete
}
}
Here externalScope is defined like this (for a scope with application lifetime):
class MyApplication : Application() {
// No need to cancel this scope as it'll be torn down with the process
val applicationScope = CoroutineScope(SupervisorJob() + otherConfig)
}
You should create a singleton Repository class, then access its instance anywhere you want.
object Repository {
val instance: Repository
get() {
return this
}
}
You can create create its object in ViewModel for A and create object in ViewModel for B.
Both will have same instance for Repository class, so in this way you can achieve what you need.
I have a single activity and multiple fragments styled application using the navigation component.
I am using Koin for my DI. I was wanting to create a Navigator class in my application as per the postulates of clean architecture.
This hypothetical class would look like :
class Navigator(private val navHostFragment: NavHostFragment)
{
fun toStudentsProfile():Unit
{
val action = HomeFragmentDirections.toStudentsProfile()
navHostFragment.findNavController().navigate(action)
}
fun toTeachersProfile():Unit
{
val action = HomeFragmentDirections.toTeachersProfile()
navHostFragment.findNavController().navigate(action)
}
}
My problem now is how should I create this under the Koin container ?
val platformModule = module {
single { Navigator("WHAT CAN BE DONE HERE") }
single { Session(get()) }
single { CoroutineScope(Dispatchers.IO + Job()) }
}
Furthermore, the Koin component would get ready before the navhostfragment is ready hence it won't be able to satisfy the dependency, to begin with.
Is there a way to provide Koin with an instance of a class and then subsequently start using it?
Koin allows to use parameters on injection
val platformModule = module {
factory { (navHostFragment: NavHostFragment) -> Navigator(navHostFragment) }
single { Session(get()) }
single { CoroutineScope(Dispatchers.IO + Job()) }
}
I have declared the dependency as factory, i guess it could be scoped to the activity as well. Declaring it as single will lead to misbehavior, as if the activity (therefore the navhostFragment) is re-created, the Navigator object will be referencing the destroyed navhostFragment.
As the fragments will be navhostFragment children, you can obtain the Navigator object in the fragments this way:
val navigator: Navigator by inject { parametersOf(requireParentFragment()) }
we are using in our project KOIN like DI library.
in some cases, when ViewModel instance not refreshing when Koin context is killing and recreating again. We need to implement feature like 'reassembling dependency graph in runtime', and this issue very critical for us.
I have ViewModel module like this:
object ViewModelModule {
val module by lazy {
module {
viewModel { AppLauncherViewModel(get(), get(), get(), get()) }
viewModel { AuthLoginPasswordViewModel(get(), get()) }
viewModel { SettingsViewModel(get(), get()) }
// some others
}
}
}
And my graph is assembling in android application by this way:
private fun assembleGraph() {
val graph = listOf(
AppModule.module,
StorageModule.module,
DatabaseConfigModule.module,
RepositoryModule.module,
InteractorModule.module,
ViewModelModule.module
)
application.startKoin(application, platformGraph)
}
fun reassembleGraph() {
stopKoin()
assembleGraph()
}
And when reassembleGraph() is calling - all good, another instances in graph are refreshing, but ViewModels, that injected in activity - are not, and they are keeping old references. I guess, that viewmodel is attached to activity lifecycle, and could help activity recreation, but i think it's not the best solution.
Has anyone the same problems? And help me please with advice, how to solve it, please.
You can do it with the use of scope in KOIN.
1) Define your ViewModels in scope
scope(named("ViewModelScope")){
viewModel {
AppLauncherViewModel(get(), get(), get(), get())
AuthLoginPasswordViewModel(get(), get())
SettingsViewModel(get(), get())
}
}
2) Create that particular scope with the use of below line in your application class.
val viewModelScope = getKoin().getOrCreateScope("ViewModelScope")
Above code is used to get ViewModel. And when you want to recreate scope you just need to close scope and recreate again. To close scope use below code.
val viewModelScopeSession = getKoin().getOrCreateScope("ViewModelScope")
viewModelScopeSession.close()
Once the scope is closed then after whenever you request to create or get scope at that time it will return new instance as per your requirement.
For further reference, you can see below link (8th Point).
Koin documentation
Guice user, trying to understand a bit of the benefits of Dagger here.
Let's say I have the following
MyActivity.java
---------------
public class MyActivity {
#Inject MyImplicitClass myImplicitClass;
#Inject #Named("foo") MyExplicitClass myNamedExplicitClass;
...
}
MyImplicitClass.java
------------
public class MyImplicitClass {
#Inject
MyImplicitClass(MyExplicitClass myExplicitClass) {
...
}
...
}
MyModule.java
---------------
#Module(injects = { ? }) {
#Provides provideExplicitClass() {
return new MyExplicitClass();
}
#Named("foo") #Provides provideNamedExplicitClass() {
return new MyExplicitClass();
}
}
So, my question is, what should go in the Injects?
I know for a fact that MyActivity needs to go. Or rather, whatever "this" needs to have DaggerInjector.inject(this)
Does anything else?
Would Dagger the implicit construction injection class (MyImplicitClass) and/or the class explicitly provided in the module (MyExplicitClass) to also be specified?
MyExplicitClass wouldn't even make sense if I need it to be annotated.
However, the javadoc makes me feel I should error the side of inclusion
http://square.github.io/dagger/javadoc/dagger/Module.html#injects()
Dagger 1 does it's validation at compile therefore it needs all of the entry point you're going to use at runtime (objectgraph.get(entrypoint), objectgraph.inject(entrypoint)).
Entry points are the injectable classes which are at the top of the dependency graph. In Android most of the time those are your Activities, Fragments, Services, Broadcast Receivers. All compontents which are not created by Dagger. You would also list classes which are not part of any entry point's dependency tree again becoming an entry point of their own.
Those entry point are use to do validation by Dagger. Dagger will start validation at the entry points and tricle down the dependency tree and validate dependencies annotated with #Inject and #Provides from your Modules.
So in conclusion only entry points needs to be provided in the #Module(injects = { * })
In your example because the MyImplicitClass is an #Inject dependency of your entry point MyActivity you don't need to specify it in the #Modules(injects={}). The same is true for your MyExplicitClass dependency. Therefore from your example the only class that needs to be added to the list is in fact MyActivity.
Base on your example code, if you had something like this.
// MyIndependantClass.java
-----------------
public class MyIndependantClass {
#Inject
MyIndependantClass(MyExplicitClass myExplicitClass) {
}
}
// MyActivity.java
---------------
public class MyActivity {
#Inject MyImplicitClass myImplicitClass;
#Inject #Named("foo") MyExplicitClass myNamedExplicitClass;
...
protected void onCreate (Bundle savedInstanceState) {
MyIndependantClass myIndependantClass = MyObjectGraph.get(MyIndependantClass.class);
...
}
}
The fact that MyIndependantClass is created using ObjectGraph.get() and is not a direct dependency of your MyActivity entry point you would need to declare that class part of the injects list.
// MyModule.java
---------------
#Module(injects = { MyActivity.class, MyIndependantClass.class }) {
#Provides provideExplicitClass() {
return new MyExplicitClass();
}
#Named("foo") #Provides provideNamedExplicitClass() {
return new MyExplicitClass();
}
}