4
votes

I'm trying to make a shared injected view model between a fragment and an activity using the Jetpack tutorial.

The shared view model is successfully injected into the parent MyActivity but when the child is rendered, the application crashes due to dependency injection failure. I have provided the code below that created the issue.

Providing the Session Manager:

@InstallIn(ApplicationComponent::class)
@Module
class AppModule {
    @Provides
    @Singleton
    fun provideSessionManager(
        networkClient: NetworkClient
    ): SessionManager {
        return SessionManager(networkClient)
    }
}

To be injected into the Shared View Model:

class SharedViewModel @ViewModelInject constructor(
    private var sessionManager: SessionManager
) : ViewModel() {

    var name = MutableLiveData<String>("Shared View Model")
}

And is used by both a parent activity and child fragment.

class MyActionFragment() : Fragment() {
    private val viewModel: SharedViewModel by viewModels()
    override fun onActivityCreated(savedInstanceState: Bundle?) {
       super.onActivityCreated(savedInstanceState)
       Timber.d("View Model Name 1: ${viewModel.name.value}") // This line crashes
    }
}
class MyActivity : AuthenticatedBaseActivity() {
    private val viewModel: SharedViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Timber.d("View Model Name 2: ${viewModel.name.value}") // This line prints
    }
}

However, when the code is run, notice the activity created the ViewModel and accessed its values, but when the fragment tried to do the same, the application crashes:

**D/MyActivity: View Model Name 2: Shared View Model**
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.xxx.xxx, PID: 16630
    java.lang.RuntimeException: Cannot create an instance of class com.xxx.xxx.ui.main.SharedViewModel
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:221)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:278)
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:54)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:41)
        at com.xxx.xxx.ui.main.MyActionFragment.getViewModel(Unknown Source:2)
        at com.xxx.xxx.ui.main.MyActionFragment.onActivityCreated(**MyActionFragment.kt:140**)
        at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:2718)

Additionally, when I remove the Hilt dependency injected sessionManager the fragment and view model are created without an issue.

Followed this post with no luck.

Any help on Hilt view model dependency injection with a shared model would be extremely appreciated!! Thanks!

3

3 Answers

5
votes

You must provide all dependency , In your case NetworkClient not provided

@Module
@InstallIn(ApplicationComponent::class)
object AppModule {

    @Singleton
    @Provides
    fun provideSessionManager(
        networkClient: NetworkClient
    ): SessionManager = SessionManager(networkClient)

    @Singleton
    @Provides
    fun provideNetworkClient() = NetworkClient()
}

In the Activity or Fragment use @AndroidEntryPoint

@AndroidEntryPoint
class MyActionFragment() : Fragment()



@AndroidEntryPoint
class MyActivity : AuthenticatedBaseActivity()
11
votes

You can use extension function in Fragment:

class MyFragment: Fragment() {
    private val viewModel: SharedViewModel by activityViewModels()
}

And in Activity:

class MyActivity : Activity() {
    private val viewModel: SharedViewModel by viewModels()
}
4
votes

To share data between activity and fragments. use the below code. Hilt doc didn't work for me also.

In Activity

    private val vm by viewModels<StartVM>()

In Fragment

private val vm: StartVM by lazy {
    obtainViewModel(requireActivity(), StartVM::class.java, defaultViewModelProviderFactory)
}

Kotlin extension

fun <T : ViewModel> Fragment.obtainViewModel(owner: ViewModelStoreOwner,
    viewModelClass: Class<T>,
    viewmodelFactory: ViewModelProvider.Factory
) =
    ViewModelProvider(owner, viewmodelFactory).get(viewModelClass)