What is the best way to use kodein with Android Dynamic feature module? Dyanamic module will be downloaded latter at some point.
Is there any way to dynamically add module to kodein dependency.
/* current code */
override val di = DI.lazy {
import(androidXModule(this#AppController))
import(timeModule)
bind { singleton { NetworkConnectionInterceptor(instance())
}
}
public void onClickDownloadFeatureModule() {
// Add it to "App level kodein
}
}
Related
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())
}
}
When using Kodein, if I have 2 modules and module B needs to use an instance from module A, is the best practice to import module A into module B or is there a better way to do it?
For example, I have a networkingModule:
val networkingModule = Kodein.Module("networking") {
bind<Retrofit>() with singleton {
Retrofit.Builder()
.baseUrl("https://api.example.com/")
.build()
}
}
And subscribersModule needs the Retrofit instance from networkingModule:
val subscribersModule = Kodein.Module("subscribersModule") {
import(networkingModule)
bind<SubscribersService>() with singleton {
instance<Retrofit>().create(SubscribersService::class.java)
}
}
Is adding the import(networkingModule) in subscribersModule the best way to do it?
At the end, if your modules are used in one project, you're not force to make them dependant.
Instead you can import them in a global container, like this:
val applicationContainer = Kodein {
import(subscribersModule)
import(networkingModule)
// ...
}
Kodein-DI will solve the dependencies for you.
Is there any way to implement Dagger's SubCompoent concept on Koin?
The thing that i want to do is using instance from parent scope's one.
app_modules.kt
val favoriteModule = module {
scope(named<FavoriteFragment>()) {
scoped { GetFavoriteMovies(get()) }
scoped { FavoriteVMFactory(get(), get()) } // This need 'MovieEntityMovieMapper'
}
}
val popularModule = module {
scope(named<PopularFragment>()) {
scoped { GetPopularMovies(get()) }
scoped { PopularVMFactory(get(), get()) } // This need 'MovieEntityMovieMapper'
}
}
val searchModule = module {
scope(named<SearchFragment>()) {
scoped { SearchMovies(get()) }
scoped { SearchVMFactory(get(), get()) } // This need 'MovieEntityMovieMapper'
}
}
val mainModule = module {
scope(named<MainActivity>()) {
scoped { MovieEntityMovieMapper() }
// this ImageLoader also injected by Fragments
scoped<ImageLoader> { (activity: Activity) -> GlideImageLoader(activity) }
}
}
Using Dagger, this can be done by SubComponent or Component Dependency.
But in Koin(especially 2.0), i cant not find way.
Some answer said use
GlobalContext.get().koin.getScope("Parent").get<>().
https://github.com/InsertKoinIO/koin/issues/513
Koin sharing instances between modules
but i dont think it's not a clean approach and a dependency injection.
You can try https://github.com/beyama/winter which is inspired by Koin but has child graphs that can be used like Dagger's subcomponents. Winter has been used in production for some major apps with several million installs for a few years now. I hope it is ok to post an example here: https://play.google.com/store/apps/details?id=de.prosiebensat1digital.seventv
Disclaimer: I know the author and have been using his library myself since its release. So far it has been stable in my experience.
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()) }
In the below shown diagram, I am having 3 modules(as android library) which extends the base "common components module" and all this 3 modules will be added to a single android application. All 3 modules are independent modules but when it comes as an application, it would require to share some data, launch other module and requires more inter-communication.
So can anyone let me know how we can implement the "Data Sharing Layer" and "Navigation Controller" in this kind of architecture?
Example: Module1 -> Login, Module2 -> Profile Management etc and there could be "n" number of modules based on the application need.
What you are looking for is basically a clean approach on how to communicate with other classes. There is not really a difference in whether or not they are in different modules.
The following sample describes how a LoginActivity could navigate to some profile activity. This is just a basic sample to be improved with what you actually need and intend to do!
Define your interfaces
Write interfaces of what you need. Your Login should be able to open a profile page? Well this sounds like it needs a LoginNavigator!
interface LoginNavigator {
void showProfile();
}
Include those interfaces in your shared components. There is not really a possibility to go without defining interfaces. You can make them more abstract or more fine grained, this is entirely up to you.
Declare your dependencies
Remember how your Login needs a LoginNavigator? The real problem is on how to supply it to your class. You should have a look at dependency injection, since there are frameworks liks dagger-2 that (could) make this easier. For now, we define an interface for a common component, so that we can retrieve the dependencies we need.
interface NavigatorProvider {
LoginNavigator provideNavigator();
}
You may guess it—this method is used to get the actual LoginNavigator that you can use to get the implementation of that interface. Usually you would just declare this dependency in the constructor, but since android is somewhat special you need to get it from somewhere yourself.
Provide your dependencies
The easiest way to go is to just have your application implement this interface (or hold an object that does).
class MyApp extends Application implements NavigatorProvider {
LoginNavigator provideNavigator() {
return new LoginNavigator() {
void showProfile() {
// just some sample code. You should probably not use an
// anonymous class
startActivity(new Intent(this, MyProfileActivity.class));
}
};
}
}
Again, you could also return an object that is implementing this interface. This is just a basic sample.
Use the interface. (And don't care about the implementation)
Now the dependency injection is nearly complete. We have an interface that we need, we have some way to provide the dependency, all that's left is to get it and use it.
class LoginActivity extends Activity {
LoginNavigator mNavigator;
void onCreate() {
// get the dependency
mNavigator = ((NavigatorProvider) getApplicationContext()).provideNavigator();
// use it where needed. (again, just sample code)
findShowProfileView().setOnClickListener(new OnClickListener() {
void onClick(View view) {
mNavigator.showProfile();
}
});
}
}
Now the dependency is provided, and ready to be used.
What this sample shows is how to basically use interfaces to decouple logic. You will still need some point of entry, since android does not allow to implement your own constructors—this is why the application class is used.
I found that solution using Local Broadcast which is implemented in Application Class and send event on Local Broadcast which is received in Application Class.
class AppApplication : Application() {
override fun onCreate() {
super.onCreate()
registerBroadcast()
}
private fun startProfileActivity() {
val intent = newIntent<MyProfileActivity>(this)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
this.startActivity(intent)
}
private fun registerBroadcast() {
LocalBroadcastManager.getInstance(this)
.registerReceiver(broadCastReceiver,IntentFilter(BROADCAST_VIEW_PROFILE))
}
private fun unregisterBroadcast() {
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(broadCastReceiver)
}
private val broadCastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
when (intent?.action) {
BROADCAST_VIEW_PROFILE -> {
startProfileActivity()
}
}
}
}
override fun onTerminate() {
super.onTerminate()
unregisterBroadcast()
}
}
When you send the event in an Application like this
private fun viewProfileEventSend() {
// Send Broadcast for view profile to `APP`
val intent = Intent(BROADCAST_VIEW_PROFILE)
LocalBroadcastManager.getInstance(requireContext()).sendBroadcast(intent)
}
Because your module doesn't need to get the instance of Application or any interface.