2

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
        //
    }
}
David
  • 3,055
  • 4
  • 30
  • 73

2 Answers2

1

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

Master Zzzing
  • 524
  • 6
  • 18
  • Thanks for your answer! So if I use Dagger for example there's no need to use `AndroidViewModel` I Could simply use `ViewModel`? and pass all the necessary objects (like `PermissionManager`) to my `viewmodel`? – David Nov 23 '19 at 09:06
  • yes, If you use Dagger, you could simply use ViewModel. A Question! if you need to check for permission, Why don't you do that in Activity directly? – Master Zzzing Nov 23 '19 at 09:10
  • if permission granted, let ViewModel do sth – Master Zzzing Nov 23 '19 at 09:11
  • I would like to have one place that has all necessary permissions of my app. – David Nov 23 '19 at 09:18
  • for me, I used static methods instead. I'm not sure it's the best practice or not but I'm happy with it – Master Zzzing Nov 23 '19 at 09:28
0

Even though your useCase is not entirely obvious, i suppose you need to invoke an function of this class, if some event is triggered by your user. Given this scenario, I would suggest the following:

<data>

    <variable
        name="receiverHandler"
        type="your.package.MyTestReiverHandler" />

    <variable
            name="viewModel"
            type="your.package.MainViewModel"/>
</data>

this will allow you to pass this receiverHandler into an function of your viewModel

<View
     android:onClick="@{viewModel.doSomething(receiverHandler)}" />

When you create your binding, you instaniate your ReceiverHandler and inject it. After building this layout-file, the fieldName of the <variable ../> is used on your binding.

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val binding = DataBindingUtil.inflate<MainFragmentBinding>(inflater, R.layout.fragment_main, container, false)
    binding.viewModel = viewModel
    binding.lifecycleOwner = this
    binding.receiverHandler = MyTestReiverHandler(container?.context?.applicationContext!!)

If you need to invoke a function by observing a property of your viewModel, you dont require injecting it into the viewModel, therefore my assumption about user interaction.

note: Any <import ..> in the <data .. /> hierarchy within the layout.xml is only neccessary, if you want to invoke functions of this particular class.

Tomes
  • 173
  • 3
  • 13