Android MVVM。传递具有 Context 作为依赖项的对象
Android MVVM. Passing objects that has Context as dependency
我有一个 viewmodel
接受模型作为构造函数参数之一。 MainModel
有 context
(我需要它来注册 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
作为参数的管理器是个好主意吗?我知道将 context
、activity
或 views
直接传递给我的 viewmodel
是不好的。但是在我的 viewModel
中有其他具有 context
或 activity
(如 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。如果你希望你可以实现依赖注入来避免这种依赖性的东西
我有一个 viewmodel
接受模型作为构造函数参数之一。 MainModel
有 context
(我需要它来注册 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
作为参数的管理器是个好主意吗?我知道将 context
、activity
或 views
直接传递给我的 viewmodel
是不好的。但是在我的 viewModel
context
或 activity
(如 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。如果你希望你可以实现依赖注入来避免这种依赖性的东西