Search code examples
androidviewmodelandroid-mvvm

Android MVVM. Passing objects that has Context as dependency


I have a viewmodel that accepts model as one of a constructor parameters. MainModel has context (I need it for registering broadcastReceiver).

class MainViewModel(private val test: MyTestReiverHandler) : ViewModel() {

}

class MyTestReiverHandler(context: Context) {
    init {
        val intentFilter = IntentFIlter()
        intentFiler.addAction("android.net.conn.CONNECTIVITY_CHANGE")
        context.registerReceiver(receiver, intentFiler)
    }

    fun doSomething() {
        // do something....
    }
}

I know that viewmodel's shouldn't have any reference to context. So is this approach is correct? Or should all the things that require context like location receiver, broadcast receiver, ... should be handled in view aka activity level? and then pass as result to viewmodel?

EDIT

I will also add other example I have PermissionManager that has activity to ask some kind of permissions. I want to use that manager for all of my viewmodels Is it a good idea to have some kind of manager that has activity as a parameter in my viewmodels?. I know that it is bad to pass context, activity or views directly to my viewmodel. But is it good to have other objects that has context or activity (like PermissionManager) in my viewModel

class MainViewModel(private val permissionManager: PermissionManager) : ViewModel() {
    fun doSomethingWithLocation() {
        permissionManager.requestLocationPermission({
            // do something if permission granted
        })
    }

    fun doSomethingWithCamera() {
        permissionManager.requestCameraPermission({
            // do something if permission granted
        })
    }
}

class DetailViewModel(private val permissionManager: PermissionManager) : ViewModel() {
    fun doSomethingWithLocation() {
        permissionManager.requestLocationPermission({
            // do something if permission granted
        })
    }

    fun doSomethingWithCamera() {
        permissionManager.requestCameraPermission({
            // do something if permission granted
        })
    }
}

class PermissionManager(activity: Activity) {

    private val activityWeakRef = WeakReference(activity)

    fun requestLocationPermission(onPermissionGranted: ((Boolean) -> Unit)) {
        //
        // Location permission implemetntation
        //
    }

    fun requestStoragePermission(onPermissionGranted: ((Boolean) -> Unit)) {
        //
        // Storage permission implemetntation
        //
    }

    fun requestCameraPermission(onPermissionGranted: ((Boolean) -> Unit)) {
        //
        // Camera permission implemetntation
        //
    }
}

Solution

  • you can extend your MainViewModel from AndroidViewModel(application: Application) instead of ViewModel. now you can use your MainViewModel just like befor and application instance can be used to register your broadcastReceiver

    class MainViewModel(application: Application) : AndroidViewModel(application) {
    
    }
    

    in activity/fragment

    val viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
    

    It's the right and easy one.

    should all the things that require context like location receiver, broadcast receiver, ... should be handled in view aka activity level?

    Not all of them. I used to work on an application that tracks user location. Since the app needs to track user location for a long period of time with other operations working at the same time. I decided to do it in another thread and let the ViewModel broadcasts the result respectively. Repository also needs context to build Room database. ViewModel will need context to instantiate the repository. That's why we have AndroidViewModel over ViewModel. If you wish you can implement Dependency Injection to avoid this dependency stuff