Viewmodels are being used more and more, and strictly speaking, it’s not officially recommended that you add a reference to the Context in your ViewModel. At the same time, ViewModel constructors take no parameters and can be very inflexible at times. Two methods are documented below.

1. By Kotlin’s extension function


fun <T : ViewModelProvider, V : ViewModel> T.get(
    key: String,
    modelClass: Class<V>,
    context: FragmentActivity
): V {
    val model = get(key, modelClass)
    if (model is TestViewModel) {
        model.addContext(context)
    }
    return model
}

fun <T : ViewModelProvider, V : ViewModel> T.get(
    key: String,
    modelClass: Class<V>,
    context: Context
): V {
    val model = get(key, modelClass)
    if (model is TestViewModel) {
        model.addContext(context)
    }
    return model
}

fun <T : ViewModelProvider, V : ViewModel> T.get(
    modelClass: Class<V>,
    context: FragmentActivity
): V {
    val model = get(modelClass)
    if (model is TestViewModel) {
        model.addContext(context)
    }
    return model
}

fun <T : ViewModelProvider, V : ViewModel> T.get(
    modelClass: Class<V>,
    context: Context
): V {
    val model = get(modelClass)
    if (model is TestViewModel) {
        model.addContext(context)
    }
    return model
}
Copy the code

Add the following method to TestViewModel

class TestViewModel : ViewModel() {
    protected lateinit var context: Context
    open fun addContext(context: FragmentActivity) {
        this.context = context
    }

    open fun addContext(context: Context) {
        this.context = context
    }
}
Copy the code

Method of use

val viewModel = ViewModelProvider(this).get(TestViewModel::class.java, this)
Copy the code

2. Through the custom ViewModelProvider. Factory

class CoreViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
    override fun 
        create(modelClass: Class<T>): T {
        try {
            for (constructor in modelClass.constructors) {
                if (arrayOf(Context::class.java).contentEquals(constructor.parameterTypes)) {
                    return (constructor as Constructor<T>).newInstance(context)
                }
            }
            return modelClass.newInstance()
        } catch (e: InstantiationException) {
            throw RuntimeException("Cannot create an instance of $modelClass", e)
        } catch (e: IllegalAccessException) {
            throw RuntimeException("Cannot create an instance of $modelClass", e)
        }
    }
}
Copy the code

For this section, if you look closely at the ViewModelProvider code, you’ll see that there are two or three factories as well. In view of the AndroidViewModel can directly with the context, provides ViewModelProvider. AndroidViewModelFactory, just at the time of reference, don’t add it again.

Here’s your TestViewModel

class TestViewModel(private val context: Context) : ViewModel() {
    init {
        L.i(" context $context ")}}Copy the code

Method of use

val viewModel = ViewModelProvider(this, CoreViewModelFactory(this)).get(TestViewModel::class.java)
Copy the code

The above two methods can also be used to help you customize some of the parameters you pass in.