3

Now I using the viewModelFactory to init the argument for viewModel in fragment.

class MyFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val binding = FragmentMyBinding.inflate(inflater)
        binding.lifecycleOwner = this
        val argument = MyFragmentArgs.fromBundle(requireArgument()).myArgument
        val viewModelFactory = MyViewModelFactory(myArgument, application)
        binding.viewModel = ViewModelProvider(
                this, viewModelFactory).get(MyViewModel::class.java)
        return binding.root
    }
}

class MyViewModelFactory(
        private val myArgument: MyArgument,
        private val application: Application) : ViewModelProvider.Factory {
    @Suppress("unchecked_cast")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MyViewModel::class.java)) {
            return MyViewModel(myArgument, application) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

Compare to the hilt dependency inject way, is there a way to pass argument to viewModel directly?

ccd
  • 5,788
  • 10
  • 46
  • 96
  • Are your dependencies runtime or buildtime. If so (build time) refer to @AliSh answer. Otherwise you have to create a viewModel factory at the moment. That's because Hilt doesn't support `@AssistedInject` yet. – coroutineDispatcher May 01 '21 at 22:08

2 Answers2

2

Yes. With Hilt, you can completely ditch the factory pattern.

To inject a param, you can use @ViewModelInject annotation, and to inject the viewModel in the activity you can use the by viewModels() method from androidx.activity package.

Here's an example.

ProductsViewModel.kt

import androidx.hilt.lifecycle.ViewModelInject

class ProductsViewModel @ViewModelInject constructor(
    private val foo: Foo
    private val bar: Bar
) : ViewModel() 

ProductsActivity.kt

@AndroidEntryPoint
class ProductsActivity : AppCompatActivity(){
    
    val viewModel: ProductsViewModel by viewModels()
    
}

To see this in action, you can checkout this repo.

theapache64
  • 10,926
  • 9
  • 65
  • 108
2

You can do it using Hilt library. You should first define your view model by @HiltViewModel and use constructor injection(@Inject for constructor of your view model):

@HiltViewModel
class MainViewModel @Inject constructor(val foo: Foo) : ViewModel()

My Foo class is:

class Foo @Inject constructor(val someDependency: Dependency)

If you have any other class which you need to inject to your ViewModel, you can add it to your hilt module.

Finally, you should instantiate your ViewModel in your activity/fragment:

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    val mainViewModel: MainViewModel by viewModels()
}

Please note that we are talking only about build time dependencies. For runtime dependencies with Hilt, you still have to stick to a classic ViewModelProvider.Factory since @AssistedInject is not yet supported by it.

coroutineDispatcher
  • 7,718
  • 6
  • 30
  • 58
AliSh
  • 10,085
  • 5
  • 44
  • 76