Reputation: 1
I am trying to implement inapp-updates, I’ve Activity which has a ViewModel I added a method to it to “checkAppUpdates” which invokes “CheckAppUpdateUseCase” which take AppUpdateManager as a constructor parameter and then calls its checkForAppUpdates which do the actual checking for updates, the implementation needs either an instance of “Activity” or ”ActivityResultLauncher” to be passed to google’s AppUpdateManager’s startUpdateFlow or startUpdateFlowForResult method respectively my problrm is with injecting activity as a constructor parameter to my AppUpdateManager but it is not working, I am getting this build error >
MyApplication_HiltComponents.java:186: error: [Dagger/MissingBinding] @dagger.hilt.android.qualifiers.ActivityContext android.app.Activity cannot be provided without an @Provides-annotated method. public abstract static class SingletonC implements FragmentGetContextFix.FragmentGetContextFixEntryPoint,
Missing binding usage: @dagger.hilt.android.qualifiers.ActivityContext android.app.Activity is injected at com.my.app.AppUpdateManager(activity) com.my.app.AppUpdateManager is injected at com.my.app.CheckAppUpdateUseCase(appUpdateManager) com.my.app.CheckAppUpdateUseCase is injected at com.my.app.MainViewModel(…, checkAppUpdateUseCase, …) com.my.app.MainViewModel is injected at com.my.app.MainViewModel_HiltModules.BindsModule.binds(arg0) @dagger.hilt.android.internal.lifecycle.HiltViewModelMap java.util.Map<java.lang.String,javax.inject.Provider<androidx.lifecycle.ViewModel>> is requested at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [com.my.app.MyApplication_HiltComponents.SingletonC → com.my.app.MyApplication_HiltComponents.ActivityRetainedC → com.my.app.MyApplication_HiltComponents.ViewModelC]
I've an activity like that
@AndroidEntryPoint
class MainActivity : AppCompatActivity(){
private val viewModel by viewModels<MainViewModel>()
...
The viewmodel
@HiltViewModel class MainViewModel @Inject constructor( private val logoutUseCase: LogoutUseCase, private val checkAppUpdateUseCase: CheckAppUpdateUseCase, ...
The use-case
class CheckAppUpdateUseCase @Inject constructor(
private val appUpdateManager: AppUpdateManager){
suspend fun checkForAppUpdates() {
appUpdateManager.checkForAppUpdates()
}
}
The AppUpdateManager
private const val UPDATE_PRIORITY_HIGH = 4
class AppUpdateManager @Inject constructor(private val activity: Activity) {
suspend fun checkForAppUpdates() {
// use the activity here
}
}
and I provide the AppUpdateManager like that
import android.app.Activity
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dagger.hilt.android.qualifiers.ActivityContext
import com.my.app.AppUpdateManager
@Module
@InstallIn(ActivityComponent::class)
object UpdateManagerModule {
@Provides
fun provideActivity(@ActivityContext activity: Activity) = activity
@Provides
fun provideUpdateManager(@ActivityContext activity: Activity): AppUpdateManager {
return AppUpdateManager(activity)
}
}
I tried to inject the Activity directly to isolate the problem and commented the unnecessary code but I got the same error.
Upvotes: 0
Views: 1070
Reputation: 1
The Librarian's answer was helpful and it directed me to think about not using the ActivityContext in the ViewModel, but this SO Answer solved my problem by changing the activity dependency to context and then casting it to activity
class AppUpdateManager @Inject constructor(@ActivityContext private val activityContext: Context) {
And in module
@Provides
fun provideAppUpdateManager(@ActivityContext context: Context): AppUpdateManager =
AppUpdateManager(context
)
Upvotes: 0
Reputation: 1898
There is no clean-cut way how to go about this.
You can't use @ActivityContext
in the ViewModel(or anything that uses it) because @ActivityContext
is @ActivityScoped
.
In the Hilt component hierarchy, @ActivityComponent is on the same level as @ViewModelComponent
- they both share @ActivityRetainedComponent
parent. https://dagger.dev/hilt/components
A child can use any bindings that are available to the parent but that's it - there is no path between siblings.
All of this is to say that Hilt doesn't have a way to pass Activity-related dependencies to the ViewModel by itself - you will have to either pass it in an argument for the checkForAppUpdates()
or some other way.
You can inject those use cases in the activity and call them directly - this way Hilt will take care of any dependencies they have(except graph hierarchy limitations again).
@ActivityScoped
class AppUpdateManager @Inject constructor(private val activity: Activity, singletonDependency: SingletonDependency) {
init {
Log.v("AppUpdateManager","init{$activity, $singletonDependency}")
}
suspend fun checkForAppUpdates() {
Log.v("AppUpdateManager","#checkForAppUpdates()")
}
}
@ActivityScoped
class CheckAppUpdateUseCase @Inject constructor(private val appUpdateManager: AppUpdateManager) {
init {
Log.v("CheckAppUpdateUseCase","init{$appUpdateManager}")
}
suspend fun checkForAppUpdates() {
Log.v("CheckAppUpdateUseCase","#checkForAppUpdates()")
appUpdateManager.checkForAppUpdates()
}
}
@AndroidEntryPoint
class FooActivity: AppCompatActivity() {
@Inject
lateinit var checkAppUpdateUseCase: CheckAppUpdateUseCase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
c.checkForAppUpdates()
}
}
}
P.S. Hilt does provide the Activity directly so sorry about my earlier statement. The activity does not require any qualifier because it can only be one - Hilt can also provide Application context.
Upvotes: 0