Closed stanete closed 6 years ago
Hello,
I'm currently writing recipes for such use. You can bind your Activity in your activity :
lateinit var
- to allow the value to be set laterclass Analytics() {
lateinit var activity: Activity
private val firebaseAnalytics: FirebaseAnalytics = FirebaseAnalytics.getInstance(activity)
fun setCurrentScreen(screenName: String) {
firebaseAnalytics.setCurrentScreen(activity, screenName, null)
}
}
class MainActivity : AppCompatActivity() {
val presenter : MainPresenter by inject()
override fun onStart() {
super.onStart()
presenter.activity = this
}
}
Thank you for your quick response. The issue is that I don't want my presenter to depend on any activity. The only class who should know about the activity is Analytics
which is the one who needs it. How can I achieve that with Koin?
I would like to avoid doing something like this:
class MainActivity : AppCompatActivity() {
val presenter : MainPresenter by inject()
override fun onStart() {
super.onStart()
presenter.analytics.activity = this
}
}
Ok. Then, if you don't want to keep in in your presenter, pass it through function parameter?
class MainPresenter(analytics: Analytics) {
fun doSomething(context : Context){
// ... use your activity here
}
}
Activity and all Android components are somewhat "outside" of Koin. The only thing that we could do, is about Fragment instance (because they are created manually).
Hmm I understand. So there is no nice way to provide an Activity to a Koin module. I don't know how Koin really works underneath yet, I'll have to study it. But it should be nice if we could develop a nice Koin solution to this. A lot of external frameworks depend on some Android component (like the Activity in the case of Firebase). Do you see it possible?
Maybe a curren-activity-provider could me implemented using ActivityLifecycleCallbacks It is just a thought, I will try it later.
Interesting idea. Don't know if it's in the scope of Koin to provide such thing, or just a recipe to help about such situation.
I think this has to be a "side" solution to implement if you want to keep an activity under the hand. This solution has to be documented for online reference.
I came accross the same issue to inject android framework components (activity, fragment, ...) in some dependencies. so it would be great is koin can support this. if you do not want to include it in the core koin project how about adding it to the android module?
in the meanwhile has someone a solution other than the already mentioned?
btw GREAT LIBRARY!!!
Similar issue trying to inject an instance of an Anko SQLite Database Helper. Would be amazing if KOIN could be Android Context-aware somehow to facilitate these kinds of dependencies.
If I understand, could be nice to allow inject current Activity, right?
given class:
class Analytics(val activity: Activity)
we could the a Koin module with currentActivity()
which resolves current Activity
:
val module = applicationContext{
factory { Analytics(currentActivity())}
}
this way, each time you ask for Analytics
instance, you will resolve it with current Activity
.
That will be the most used case with activity.
But maybe koin could provide a more generic way to inject also other classes
val module = applicationContext {
factory { Analytics(currentContext<GenericType>()) }
}
The provided (GenericType) class is the class where your Analytics class will be injected, e.g.
class AnalyticsActivity : Activity, Navigator {
val analytics: Analytics by inject() // in this case GenericType can be (Analytics-)Activity or Navigator
}
class AnalyticsFragment : Fragment, Navigator {
val analytics: Analytics by inject() // in this case GenericType can be (Analytics-)Fragment or Navigator
}
Can this be achieved with koin?
In Dagger, If want depend to Activity, At the timing of injecting Presenter, providing an instance of Activity to inject()
and solve it.
The javadoc of AndroidInjector<T>.Builder#seedInstance(T)
may be helpful.
https://github.com/google/dagger/blob/master/java/dagger/android/AndroidInjector.java
Great idea! Maybe we could add an optional parameter (varargs or single value) to the inject delegate to provide additional context related objects
class AnalyticsActivity : Activity, Navigator {
val analytics: Analytics by inject(this, this as Navigator, ...) // pass additional optional parameters to the inject delegate
}
I 've add Koin parameters to release 0.9.0.
you will be able to use parameters in your definition. Given class:
class MyPresenter(val activity : MyActivity)
We can use parameters to be injected with by inject()
val module = applicationContext {
factory { params -> MyPresenter(params["activity"])}
}
Injecting the parameter:
class MyActivity : AppCompatActivity(){
// Ask for MyPresenter injection and provide parameters
val presenter : MyPresenter by inject( parameters = mapOf("activity" to this))
}
stay tunedđź‘Ť
Great to hear!
Any timetable when 0.9.0 will be released?
release 0.9.0
is scheduled for end of this week or next week.
"parameters" feature will be available for viewModels too ??? I noticed in v0.9.0-rc1 viewModels are being created with emptyMap of parameters. Thanks
Yes ;) đź‘Ť Currently merging some feature branches
On Tue, 27 Feb 2018 at 17:28 fredy-mederos notifications@github.com wrote:
"parameters" feature will be available for viewModels too ??? I noticed in v0.9.0-rc1 viewModels are being created with emptyMap of parameters. Thanks
— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/Ekito/koin/issues/49#issuecomment-368937630, or mute the thread https://github.com/notifications/unsubscribe-auth/ACQFcPag2m-N73VWGbhYT8hVMMThucr_ks5tZC0NgaJpZM4SCHgP .
--
Arnaud GIULIANI - Mobile & Cloud Tel : +33 6 78 81 72 12 - mail : agiuliani@ekito.fr EKITO - 15 rue Gabriel PĂ©ri 31000 Toulouse web : www.ekito.fr / www.ekito.fr/people
Is this still the best way to achieve this?
I made a little helper:
inline fun <reified T> Activity.injectActivity(): Lazy<T> =
inject(parameters = { mapOf("activity" to this) })
class MyActivity : AppCompatActivity(){
// Ask for MyPresenter injection and provide parameters
val presenter : MyPresenter by injectActivity()
}
I am using parameters
like below @westonal.
factory { (activity: MainActivity) -> FusedLocationProviderClient(activity) }
val provider: FusedLocationProviderClient by inject { parametersOf(this@MainActivity) }
Yes, injection parameters are the best in that case!
Is there a way to create dependencies for scopes? Passing data around with custom strings optional parameters looks weird, is there a way to pass scope-local dependencies to create scope, and use it for dependencies inside this scope?
I have similar usecase as above, but activity injects presenter, that depends on LocationProvider, that uses FusedLocationClient, requiring activity. So Activity should be part of the scope, not part of specific inject.
^ agree, it would be nice if we could start a scope with a number of module params, in the same way we call startKoin()
@dgngulcan thx
How can the initial situation be solved? I have a similar situation: there is Interactor that needs activity reference and I'm injecting this interactor into viewModel constructor, but this way I can't use custom arguments.
BillingInteractor(activity: Activity) {
...
}
MyViewModel(private val billingInteractor: BillingInteractor) : ViewModel() {
...
}
@SYtor you could do something like:
fun provideYourModule() = module {
viewModel { (activity: Activity) ->
MyViewModel(billingInteractor = BillingInteractor(activity))
}
}
and in your activity:
private val myViewModel: MyViewModel by viewModel { parametersOf(this@Activity)}
@bkoruznjak
Please be aware that your solution may cause your activity to leak. For example if the screen was rotated. In this case, an Activity
will be recreated but the new Activity
will get the same instance of MyViewModel
, which still holds a reference to the old Activity
which will prevent it from being garbage collected.
@makaroffandrey makes a good point. @arnaudgiuliani any suggestion to avoid leaks?
@moizalidv
I ended up creating an extension function that creates a custom scope that is tied to the Activity
and has the Activity
itself in the dependency tree. This allows for the injection of Activity
-dependent components without leaks.
https://github.com/InsertKoinIO/koin/issues/428#issuecomment-595950513
It's a shame that Koin cannot do something like that out of the box.
You can do this with scope
.
For that your activity must extends ScopeActivity
For example, class MainActivity : ScopeActivity()
My example is for MainNavigation:
class MainNavigationImpl(private val navController: NavController) : MainNavigation
my module
will looks like:
val presentationModule = module {
scope<MainActivity> {
scoped<MainNavigation> {
val navController = get<MainActivity>().findNavController(R.id.root_navigation_host_fragment)
MainNavigationImpl(navController)
}
}
scope<ChatsFragment> {
scoped { ChatsPresenter(get()) }
}
}
then I can easily use my dependency (for example, MainNavigation):
class ChatsPresenter(private val navigation: MainNavigation)
Hi,
Koin is awesome but I'm having an issue. Some libraries, like Firebase Analytics, depend on the current Activity to perform some actions like setting a current screen.
Is this possible with Koin? If yes, then I don't see how.