Android MVVM。传递具有 Context 作为依赖项的对象

Android MVVM. Passing objects that has Context as dependency

我有一个 viewmodel 接受模型作为构造函数参数之一。 MainModelcontext(我需要它来注册 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....
    }
}

我知道 viewmodel's 不应该引用 context。那么这种做法是否正确呢? 或者所有需要 context 的东西,比如定位接收器、广播接收器……应该在 view 又名 activity 级别处理?然后作为结果传递给 viewmodel?

编辑

我还将添加其他示例,我有 PermissionManager 有 activity 来询问某种权限。我想为我的所有 viewmodels 使用该管理器,在我的 viewmodels 中使用某种具有 activity 作为参数的管理器是个好主意吗?我知道将 contextactivityviews 直接传递给我的 viewmodel 是不好的。但是在我的 viewModel

中有其他具有 contextactivity(如 PermissionManager)的对象是否好
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
        //
    }
}

即使您的 useCase 不是很明显,我想您需要调用这个 class 的函数,如果您的用户触发了某个事件。 鉴于这种情况,我建议如下:

<data>

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

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

这将允许您将此 receiverHandler 传递到您的 viewModel 的函数中

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

创建绑定时,您会实例化 ReceiverHandler 并将其注入。 构建此布局文件后,<variable ../> 的字段名称将用于您的绑定。

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!!)

如果您需要通过观察 viewModel 的 属性 来调用函数,则不需要将其注入 viewModel,因此我假设了用户交互。

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

您可以从 AndroidViewModel(application: Application) 而不是 ViewModel 扩展您的 MainViewModel。 现在你可以像以前一样使用你的 MainViewModel 并且 application 实例可以用来注册你的 broadcastReceiver

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

}

activity/fragment

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

这是正确且简单的方法。

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

不是全部。我曾经开发过一个跟踪用户位置的应用程序。由于应用程序需要在很长一段时间内跟踪用户位置,同时执行其他操作。我决定在另一个线程中进行,让 ViewModel 分别广播结果。 Repository 还需要上下文来构建 Room 数据库。 ViewModel 需要上下文来实例化存储库。这就是为什么我们有 AndroidViewModel 而不是 ViewModel。如果你希望你可以实现依赖注入来避免这种依赖性的东西