I have a BaseFragment and BaseViewModel. I created HomeFragment that extended from BaseFragment. Now I observe a LiveData of BaseViewModel but observing data is not working.
// BaseFragment
abstract class BaseFragment<VDB: ViewDataBinding, VM: BaseViewModel>: Fragment() {
lateinit var mBinding: VDB
lateinit var mViewModel: VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val parameterizedType = javaClass.genericSuperclass as? ParameterizedType
#Suppress("UNCHECKED_CAST")
val vmClass = parameterizedType?.actualTypeArguments?.getOrNull(1) as? Class<VM>?
if (vmClass != null) mViewModel = ViewModelProvider(this)[vmClass]
}
......
}
//BaseViewModel
abstract class BaseViewModel(application: Application) : AndroidViewModel(application) {
val isMeet = MutableLiveData(true)
//.... code to change isMeet ....
//HomeFragment
class MainFragment: BaseFragment<FragmentMainBinding, MainViewModel>() {
override fun onResume() {
super.onResume()
mViewModel.isMeet.observe(viewLifecycleOwner) {
Log.d(TAG, "setupViews: a $it") // there isn't any happened
}
Log.d(TAG, "onResume: ")
}
Could you please try this?
val isMeet = MutableLiveData<Boolean>().apply { value = true }
I'm getting error "Can't access ViewModels from detached fragment" when onClick method implemented by AirportOnClickListener try do do anything with viewmodel (trigger event, set var etc). What's wrong with my code? I'm using hilt for DI
#AndroidEntryPoint
class SearchFragment #Inject constructor() : Fragment(), AirportOnClickListener {
#Inject
lateinit var adapter: RecyclerViewAdapter
private var _binding: FragmentAirportSearchBinding? = null
private val binding get() = _binding!!
private val airportSearchViewModel by viewModels<AirportSearchViewModel>()
private val args: SearchFragmentArgs by navArgs()
// recyclerview row click cause error "java.lang.IllegalStateException: Can't access ViewModels from detached fragment"
// when doing anything with airportSearchViewModel
override fun onClick(airport: Airport) {
airportSearchViewModel.setAirportTye(AirportType.Origin) //ERROR
airportSearchViewModel.triggerNavigation() //ERROR
Log.d("Selected airport", airport.name) //WORKS
}
class RecyclerViewAdapter #Inject constructor(private val airportOnClickListener: Provider<AirportOnClickListener>) :
RecyclerView.Adapter<SearchViewHolder>() {
var list = emptyList<Airport>()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
val binding = RowAirportSearchBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return SearchViewHolder(binding, airportOnClickListener.get())
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
holder.bind(list[position])
}
}
class SearchViewHolder(
private val binding: RowAirportSearchBinding,
private val airportOnClickListener: AirportOnClickListener,
) :
RecyclerView.ViewHolder(binding.root) {
fun bind(airport: Airport) {
with(binding) {
city.text = airport.city.name
country.text = airport.country.name
code.text = airport.code
root.setOnClickListener {
airportOnClickListener.onClick(airport)
}
}
}
}
#Module
#InstallIn(FragmentComponent::class)
abstract class AirportListenerModule {
#Binds
abstract fun bindClickListener(
searchFragment: SearchFragment,
): AirportOnClickListener
}
kotlin.UninitializedPropertyAccessException: lateinit property airportSearchViewModel has not been initialized
at com.jurgielewiczp.androidchallenge.views.search.SearchFragment.getAirportSearchViewModel(SearchFragment.kt:37)
at com.jurgielewiczp.androidchallenge.views.search.SearchFragment.onClick(SearchFragment.kt:44)
at com.jurgielewiczp.androidchallenge.views.search.SearchViewHolder.bind$lambda-1$lambda-0(RecyclerViewAdapter.kt:49)
at com.jurgielewiczp.androidchallenge.views.search.SearchViewHolder.$r8$lambda$dVFh158DngIJuL9pfu4VOonhCvg(Unknown Source:0)
at com.jurgielewiczp.androidchallenge.views.search.SearchViewHolder$$ExternalSyntheticLambda0.onClick(Unknown Source:4)
at android.view.View.performClick(View.java:6614)
at android.view.View.performClickInternal(View.java:6587)
at android.view.View.access$3100(View.java:787)
at android.view.View$PerformClick.run(View.java:26122)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:201)
at android.app.ActivityThread.main(ActivityThread.java:6831)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:927)
when I run this class, I'm always got test failed in method verify_on_success_is_called() with error,
Actually, there were zero interactions with this mock.
but if I run method only, test will passed.
#Mock
lateinit var mDummy: Dummy
private lateinit var mainViewModel: MainViewModel
#Mock
lateinit var main: MainViewModel.IMain
#Before
#Throws(Exception::class)
fun setup() {
MockitoAnnotations.initMocks(this)
MainViewModel.mIMain = main
RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
mainViewModel = MainViewModel(mDummy)
}
#Test
fun verify_on_success_is_called() {
val mockList: ArrayList<Employee> = ArrayList()
mockList.add(Employee(1, "a", 20000.0, 22))
val list: List<Employee> = mockList
`when`(mDummy.getEmployees()).thenReturn(Observable.just(Response.success(list)))
mainViewModel.getEmployees()
Mockito.verify(main, times(1)).onSuccess()
}
#Test
fun verify_on_onError_is_called() {
MainViewModel.mIMain = main
`when`(mDummy.getEmployees()).thenReturn(Observable.error(Throwable()))
mainViewModel.getEmployees()
Mockito.verify(main, times(1)).onError()
}
this the viewModel class I want to test
class MainViewModel(private val mDummy: Dummy) : ViewModel() {
companion object {
lateinit var mIMain: IMain
}
interface IMain {
fun onSuccess()
fun onError()
}
fun getEmployees() {
mDummy.getEmployees()
.observeOn(SchedulerProvides.main())
.subscribeOn(SchedulerProvides.io())
.subscribe({ response ->
if (response.isSuccessful) {
mIMain.onSuccess()
} else {
mIMain.onError()
}
}, {
mIMain.onError()
})
}
and this my mainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
MainViewModel.mIMain = mIMainViewModelIniliazed()
}
private fun mIMainViewModelIniliazed() = object :MainViewModel.IMain{
override fun onSuccess() {
}
override fun onError() {
}
}
Please correct me if am wrong but i think your problem is because you're setting
MainViewModel.mIMain = main
before creating your viewmodel instance, shouldn't be as below?
mainViewModel = MainViewModel(mDummy)
mainViewModel.mIMain = main
I am getting an exception in the first line of the code below
viewModel.homeLiveData.observe(this, Observer { list ->
list?.let {
mList.addAll(list)
adapter.notifyDataSetChanged()
}
})
java.lang.ClassCastException: java.lang.Class cannot be cast to androidx.lifecycle.ViewModel
The Whole code is Below
what is wrong with the cast ? is there anything wrong of I am creating my ViewModel?
My BaseActivity
abstract class BaseActivity<V : ViewModel> : DaggerAppCompatActivity(), HasSupportFragmentInjector {
#Inject
lateinit var fragmentAndroidInjector: DispatchingAndroidInjector<Fragment>
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
#LayoutRes
abstract fun layoutRes(): Int
protected lateinit var viewModel : V
protected abstract fun getViewModel() : Class<V>
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == android.R.id.home)
onBackPressed()
return super.onOptionsItemSelected(item)
}
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(layoutRes())
viewModel = ViewModelProviders.of(this, viewModelFactory).get(getViewModel())
}
override fun supportFragmentInjector(): AndroidInjector<Fragment> = fragmentAndroidInjector
}
then My Activity
class MainActivity : BaseActivity<MainViewModel>() {
override fun layoutRes(): Int = R.layout.activity_main
override fun getViewModel(): Class<MainViewModel> = MainViewModel::class.java
private val mList = mutableListOf<Any>()
private lateinit var adapter: DataAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setSupportActionBar(toolbar)
recyclerView.apply {
layoutManager = GridLayoutManager(applicationContext, 2)
adapter = DataAdapter(context, mList)
}
viewModel.homeLiveData.observe(this, Observer { list ->
list?.let {
mList.addAll(list)
adapter.notifyDataSetChanged()
}
})
viewModel.getHomeItems()
}
and this is my ViewModel
class MainViewModel #Inject constructor() : ViewModel() {
val homeLiveData: MutableLiveData<List<HomeScreenModel>> = MutableLiveData()
fun getHomeItems() {
Handler().post {
val homeModleList = listOf(
HomeScreenModel(R.drawable.ic_launcher_background, MyApplication.instance.getString(R.string.settings))
)
homeLiveData.setValue(homeModleList)
}
}
}
In my opinion, your viewModel property, which you try to access in viewModel.homeLiveData is shadowed by getViewModel() abstract function that you declare in BaseActivity. This is because Kotlin thinks that getXyZ() is a getter for the field xyZ and thus when you access viewModel, the compiler thinks you want to call getViewModel, which is of type Class<V>. I suggest renaming either the function or the property and it should work then.
Check your viewModel factory if you implemented it correctly
Is there a way to pass additional argument to my custom AndroidViewModel constructor except Application context.
Example:
public class MyViewModel extends AndroidViewModel {
private final LiveData<List<MyObject>> myObjectList;
private AppDatabase appDatabase;
public MyViewModel(Application application, String param) {
super(application);
appDatabase = AppDatabase.getDatabase(this.getApplication());
myObjectList = appDatabase.myOjectModel().getMyObjectByParam(param);
}
}
And when I want to user my custom ViewModel class I use this code in my fragment:
MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)
So I don't know how to pass additional argument String param into my custom ViewModel. I can only pass Application context, but not additional arguments. I would really appreciate any help. Thank you.
Edit: I've added some code. I hope it's better now.
You need to have a factory class for your ViewModel.
public class MyViewModelFactory implements ViewModelProvider.Factory {
private Application mApplication;
private String mParam;
public MyViewModelFactory(Application application, String param) {
mApplication = application;
mParam = param;
}
#Override
public <T extends ViewModel> T create(Class<T> modelClass) {
return (T) new MyViewModel(mApplication, mParam);
}
}
And when instantiating the view model, you do like this:
MyViewModel myViewModel = ViewModelProvider(this, new MyViewModelFactory(this.getApplication(), "my awesome param")).get(MyViewModel.class);
For kotlin, you may use delegated property:
val viewModel: MyViewModel by viewModels { MyViewModelFactory(getApplication(), "my awesome param") }
There's also another new option - to implement HasDefaultViewModelProviderFactory and override getDefaultViewModelProviderFactory() with the instantiation of your factory and then you would call ViewModelProvider(this) or by viewModels() without the factory.
Implement with Dependency Injection
This is more advanced and better for production code.
Dagger2, Square's AssistedInject offers a production-ready implementation for ViewModels that can inject necessary components such as a repository that handles network and database requests. It also allows for the manual injection of arguments/parameters in the activity/fragment. Here's a concise outline of the steps to implement with code Gists based on Gabor Varadi's detailed post, Dagger Tips.
Dagger Hilt, is the next generation solution, in alpha as of 7/12/20, offering the same use case with a simpler setup once the library is in release status.
Implement with Lifecycle 2.2.0 in Kotlin
Passing Arguments/Parameters
// Override ViewModelProvider.NewInstanceFactory to create the ViewModel (VM).
class SomeViewModelFactory(private val someString: String): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = SomeViewModel(someString) as T
}
class SomeViewModel(private val someString: String) : ViewModel() {
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory("someString") }
}
Enabling SavedState with Arguments/Parameters
class SomeViewModelFactory(
private val owner: SavedStateRegistryOwner,
private val someString: String) : AbstractSavedStateViewModelFactory(owner, null) {
override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, state: SavedStateHandle) =
SomeViewModel(state, someString) as T
}
class SomeViewModel(private val state: SavedStateHandle, private val someString: String) : ViewModel() {
val feedPosition = state.get<Int>(FEED_POSITION_KEY).let { position ->
if (position == null) 0 else position
}
init {
//TODO: Use 'someString' to init process when VM is created. i.e. Get data request.
}
fun saveFeedPosition(position: Int) {
state.set(FEED_POSITION_KEY, position)
}
}
class Fragment: Fragment() {
// Create VM in activity/fragment with VM factory.
val someViewModel: SomeViewModel by viewModels { SomeViewModelFactory(this, "someString") }
private var feedPosition: Int = 0
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
someViewModel.saveFeedPosition((contentRecyclerView.layoutManager as LinearLayoutManager)
.findFirstVisibleItemPosition())
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
feedPosition = someViewModel.feedPosition
}
}
For one factory shared between multiple different view models I'd extend mlyko's answer like this:
public class MyViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private Application mApplication;
private Object[] mParams;
public MyViewModelFactory(Application application, Object... params) {
mApplication = application;
mParams = params;
}
#Override
public <T extends ViewModel> T create(Class<T> modelClass) {
if (modelClass == ViewModel1.class) {
return (T) new ViewModel1(mApplication, (String) mParams[0]);
} else if (modelClass == ViewModel2.class) {
return (T) new ViewModel2(mApplication, (Integer) mParams[0]);
} else if (modelClass == ViewModel3.class) {
return (T) new ViewModel3(mApplication, (Integer) mParams[0], (String) mParams[1]);
} else {
return super.create(modelClass);
}
}
}
And instantiating view models:
ViewModel1 vm1 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), "something")).get(ViewModel1.class);
ViewModel2 vm2 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123)).get(ViewModel2.class);
ViewModel3 vm3 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123, "something")).get(ViewModel3.class);
With different view models having different constructors.
Based on #vilpe89 the above Kotlin solution for AndroidViewModel cases
class ExtraParamsViewModelFactory(
private val application: Application,
private val myExtraParam: String
): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
SomeViewModel(application, myExtraParam) as T
}
Then a fragment can initiate the viewModel as
class SomeFragment : Fragment() {
// ...
private val myViewModel: SomeViewModel by viewModels {
ExtraParamsViewModelFactory(this.requireActivity().application, "some string value")
}
// ...
}
And then the actual ViewModel class
class SomeViewModel(application: Application, val myExtraParam:String) : AndroidViewModel(application) {
// ...
}
Or in some suitable method ...
override fun onActivityCreated(...){
// ...
val myViewModel = ViewModelProvider(this, ExtraParamsViewModelFactory(this.requireActivity().application, "some string value")).get(SomeViewModel::class.java)
// ...
}
I made it a class in which the already created object is passed.
private Map<String, ViewModel> viewModelMap;
public ViewModelFactory() {
this.viewModelMap = new HashMap<>();
}
public void add(ViewModel viewModel) {
viewModelMap.put(viewModel.getClass().getCanonicalName(), viewModel);
}
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
for (Map.Entry<String, ViewModel> viewModel : viewModelMap.entrySet()) {
if (viewModel.getKey().equals(modelClass.getCanonicalName())) {
return (T) viewModel.getValue();
}
}
return null;
}
And then
ViewModelFactory viewModelFactory = new ViewModelFactory();
viewModelFactory.add(new SampleViewModel(arg1, arg2));
SampleViewModel sampleViewModel = ViewModelProviders.of(this, viewModelFactory).get(SampleViewModel.class);
The proper way is to use a dependency injection framework such as Dagger hilt. If not using a DI framework, then do it with ViewModelFactory.
With Dagger Hilt:
A ViewModel with parameters
#HiltViewModel
class MyViewModel #Inject constructor(
private val myRepository: MyRepository,
private val savedStateHandle: SavedStateHandle
) : ViewModel() { ... }
A Repository
class MyRepository #Inject constructor(
private val myRemoteDataSource: MyDataSource,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) { ... }
A Module for providing the dependencies/parameters so they can be injected into repositories and ViewModels.
#InstallIn(ViewModelComponent::class)
#Module
object MyProvideModule {
#Provides
fun provideMyDataSource(#ApplicationContext context: Context): MyDataSource {
//code to create MyDataSource...
return MyDataSource(context)
}
#Provides
fun provideCoroutineDispatcher(): CoroutineDispatcher {
return Dispatchers.IO
}
}
A module for binding the repository
#Module
#InstallIn(ViewModelComponent::class)
interface RepositoryModules {
#Binds
fun provideMyRepository(repository: MyRepository): MyRepository
}
Initiating Dagger hilt with the application with the #HiltAndroidApp annotation.
#HiltAndroidApp
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
}
}
Getting the ViewModel in activities
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val myViewModel: MyViewModel by viewModels()
// Other code...
}
Getting the ViewModel in fragments
#AndroidEntryPoint
class MyFragment : Fragment() {
private val myViewModel: MyViewModel by activityViewModels()
// Other code...
}
With ViewModelFactory:
A ViewModel with parameter messageDataStore, where MessageDataStore is a DataStore class or it can be anything else that you want to pass into the ViewModel.
class MyViewModel(
private val messageDataStore: MessageDataStore,
): ViewModel() { ... }
The ViewModel factory class for creating ViewModels
/**
* Factory for all ViewModels.
*/
#Suppress("UNCHECKED_CAST")
class ViewModelFactory constructor(
private val messageDataStore: MessageDataStore,
owner: SavedStateRegistryOwner,
defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
override fun <T : ViewModel> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
) = with(modelClass) {
when {
isAssignableFrom(MyViewModel::class.java) ->
MyViewModel(messageDataStore)
else ->
throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
}
} as T
}
The application class for creating the dependencies/parameters
class MyApp : Application() {
val messageDataStore: MessageDataStore
get() = MessageDataStore.getInstance(this)
}
Extension functions for getting the factory class in activities and fragments, MyExt.kt
fun AppCompatActivity.getViewModelFactory(savedInstanceState: Bundle?): ViewModelFactory {
val messageDataStore = (applicationContext as MyApp).messageDataStore
return ViewModelFactory(messageDataStore, this, savedInstanceState)
}
fun Fragment.getViewModelFactory(savedInstanceState: Bundle?): ViewModelFactory {
val messageDataStore = (requireContext().applicationContext as MyApp).messageDataStore
return ViewModelFactory(messageDataStore, this.requireActivity(), savedInstanceState)
}
Getting the ViewMode in activities
class MainActivity : AppCompatActivity() {
private lateinit var myViewModel: MyViewModel
// Other code...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vm by viewModels<MyViewModel> { getViewModelFactory(savedInstanceState) }
myViewModel = vm
// Other code...
}
}
Getting the ViewModel in Fragments.
class MyFragment : Fragment() {
private lateinit var myViewModel: MyViewModel
//Other code...
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val vm by activityViewModels<MyViewModel> { getViewModelFactory(savedInstanceState) }
myViewModel = vm
//Other code...
}
}
(KOTLIN) My solution uses little bit of Reflection.
Lets say you don't want to create the same looking Factory class every time you create new ViewModel class which needs some arguments. You can accomplish this via Reflection.
For example you would have two different Activities:
class Activity1 : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = Bundle().apply { putString("NAME_KEY", "Vilpe89") }
val viewModel = ViewModelProviders
.of(this, ViewModelWithArgumentsFactory(args))
.get(ViewModel1::class.java)
}
}
class Activity2 : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = Bundle().apply { putInt("AGE_KEY", 29) }
val viewModel = ViewModelProviders
.of(this, ViewModelWithArgumentsFactory(args))
.get(ViewModel2::class.java)
}
}
And ViewModels for those Activities:
class ViewModel1(private val args: Bundle) : ViewModel()
class ViewModel2(private val args: Bundle) : ViewModel()
Then the magic part, Factory class's implementation:
class ViewModelWithArgumentsFactory(private val args: Bundle) : NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
try {
val constructor: Constructor<T> = modelClass.getDeclaredConstructor(Bundle::class.java)
return constructor.newInstance(args)
} catch (e: Exception) {
Timber.e(e, "Could not create new instance of class %s", modelClass.canonicalName)
throw e
}
}
}
In Kotlin, since the caller of the ViewModel and the ViewModel itself run in different coroutines, it is more natural and convenient to pass data between them using kotlinx.coroutines.channels.Channel:
class NewViewModel : ViewModel() {
private val newData: MutableLiveData<Service.DataEntry?> by lazy {
MutableLiveData<Service.DataEntry?>().also {
viewModelScope.launch {
val channel = Service.ParamChannel // type Channel<Params>
val params = channel.receive()
it.value = Service.postSomething(params)
}
}
}
fun getData(): LiveData<Service.DataEntry?> {
return newData
}
}
// Calling code:
val model: NewViewModel by viewModels()
model.getData().observe(this) { newData ->
if (newData != null) {
...
}
else
{
...
}
}
runBlocking {
Service.ParamChannel.send(theParams)
}
This is part of working code which I anonymized for demo purposes.
I wrote a library that should make doing this more straightforward and way cleaner, no multibindings or factory boilerplate needed, while working seamlessly with ViewModel arguments that can be provided as dependencies by Dagger:
https://github.com/radutopor/ViewModelFactory
#ViewModelFactory
class UserViewModel(#Provided repository: Repository, userId: Int) : ViewModel() {
val greeting = MutableLiveData<String>()
init {
val user = repository.getUser(userId)
greeting.value = "Hello, $user.name"
}
}
In the view:
class UserActivity : AppCompatActivity() {
#Inject
lateinit var userViewModelFactory2: UserViewModelFactory2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
appComponent.inject(this)
val userId = intent.getIntExtra("USER_ID", -1)
val viewModel = ViewModelProviders.of(this, userViewModelFactory2.create(userId))
.get(UserViewModel::class.java)
viewModel.greeting.observe(this, Observer { greetingText ->
greetingTextView.text = greetingText
})
}
}
Why not do it like this:
public class MyViewModel extends AndroidViewModel {
private final LiveData<List<MyObject>> myObjectList;
private AppDatabase appDatabase;
private boolean initialized = false;
public MyViewModel(Application application) {
super(application);
}
public initialize(String param){
synchronized ("justInCase") {
if(! initialized){
initialized = true;
appDatabase = AppDatabase.getDatabase(this.getApplication());
myObjectList = appDatabase.myOjectModel().getMyObjectByParam(param);
}
}
}
}
and then use it like this in two steps:
MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)
myViewModel.initialize(param)