如何从可打包数据初始化 androidx ViewModel?
How can I initialize an androidx ViewModel from parcelable data?
在我的 Android 应用程序中,我使用 parcelable 接口将自定义数据 (UByteArray) 从一个 activity 传递到另一个。
我在多个片段中使用此数据,因此我重写了数据 class 以扩展 androidx ViewModel 并将 LiveData 属性公开给片段。现在 UI 更新好多了,但我想我用错了,因为我覆盖了 onCreate
.
中的所有 ViewModel 值
现在我的问题是:我需要更改什么才能仅初始化一次 ViewModel?
以下是我当前的代码(针对这个问题进行了缩写和重命名):
class ActivityB : AppCompatActivity() {
private val bData: ViewModelB by viewModels()
// ...
override fun onCreate(savedInstanceState: Bundle?) {
// ...
intent.getParcelableExtra<ViewModelB>("id")?.let {
Log.e(TAG, "Found parceled bData $it")
// This seems like a very stupid way to do it, is there a better one?
bData.copyAll(it)
}
}
}
我看到可以将SavedState注入到ViewModelB构造函数中,但是我到现在都没有保存状态,数据只需要传递一次。
我是否应该将 by viewModels()
的 tagData 的初始化更改为 = ViewModelB(intent)
?
还是我需要以某种方式扩展 ViewModelFactory?
如有任何提示,我们将不胜感激,谢谢。
因为你有一个实现 Parcelable
的 ViewModel
,你可以直接从 Intent
extra 中获取你的 ViewModelB
实例。
用于启动ActivityB
的Intent
在实例化ActivityB
时可能不是!= null
,但可以使用
lateinit var bData: ViewModelB
然后在onCreate()
bData = if(intent.hasExtra("id")) intent.getParcelableExtra<ViewModelB>("id") else ViewModelProvider(this).get(ViewModelB::class.java)
I saw that it is possible to inject SavedState into the ViewModelB constructor, but I don't have a saved state until now, and the data needs to be passed only once.
官方的解决方案是提供一个 SavedStateHandle
,它用 defaultArgs
作为 Activity 的 intent.extras
初始化。
为此,您需要提供一个 AbstractSavedStateViewModelFactory
实现,或者使用 SavedStateViewModelFactory
(在这种情况下,您必须定义正确的构造函数以便通过反射实例化它)。
class ActivityB : AppCompatActivity() {
private val bData: ViewModelB by viewModels {
SavedStateViewModelFactory(application, this, intent.extras)
}
// ...
override fun onCreate(savedInstanceState: Bundle?) {
// ...
// intent.getParcelableExtra<ViewModelB>("id")?.let {
// Log.e(TAG, "Found parceled bData $it")
}
}
然后在你的 ViewModel 中
@Keep
class ViewModelB(val savedStateHandle: SavedStateHandle): ViewModel() {
val uByteData = savedStateHandle.get<UByteArray>("id")
}
左右。 "id"
键必须与 intent extras 中的键匹配。
在我的 Android 应用程序中,我使用 parcelable 接口将自定义数据 (UByteArray) 从一个 activity 传递到另一个。
我在多个片段中使用此数据,因此我重写了数据 class 以扩展 androidx ViewModel 并将 LiveData 属性公开给片段。现在 UI 更新好多了,但我想我用错了,因为我覆盖了 onCreate
.
现在我的问题是:我需要更改什么才能仅初始化一次 ViewModel? 以下是我当前的代码(针对这个问题进行了缩写和重命名):
class ActivityB : AppCompatActivity() {
private val bData: ViewModelB by viewModels()
// ...
override fun onCreate(savedInstanceState: Bundle?) {
// ...
intent.getParcelableExtra<ViewModelB>("id")?.let {
Log.e(TAG, "Found parceled bData $it")
// This seems like a very stupid way to do it, is there a better one?
bData.copyAll(it)
}
}
}
我看到可以将SavedState注入到ViewModelB构造函数中,但是我到现在都没有保存状态,数据只需要传递一次。
我是否应该将 by viewModels()
的 tagData 的初始化更改为 = ViewModelB(intent)
?
还是我需要以某种方式扩展 ViewModelFactory?
如有任何提示,我们将不胜感激,谢谢。
因为你有一个实现 Parcelable
的 ViewModel
,你可以直接从 Intent
extra 中获取你的 ViewModelB
实例。
用于启动ActivityB
的Intent
在实例化ActivityB
时可能不是!= null
,但可以使用
lateinit var bData: ViewModelB
然后在onCreate()
bData = if(intent.hasExtra("id")) intent.getParcelableExtra<ViewModelB>("id") else ViewModelProvider(this).get(ViewModelB::class.java)
I saw that it is possible to inject SavedState into the ViewModelB constructor, but I don't have a saved state until now, and the data needs to be passed only once.
官方的解决方案是提供一个 SavedStateHandle
,它用 defaultArgs
作为 Activity 的 intent.extras
初始化。
为此,您需要提供一个 AbstractSavedStateViewModelFactory
实现,或者使用 SavedStateViewModelFactory
(在这种情况下,您必须定义正确的构造函数以便通过反射实例化它)。
class ActivityB : AppCompatActivity() {
private val bData: ViewModelB by viewModels {
SavedStateViewModelFactory(application, this, intent.extras)
}
// ...
override fun onCreate(savedInstanceState: Bundle?) {
// ...
// intent.getParcelableExtra<ViewModelB>("id")?.let {
// Log.e(TAG, "Found parceled bData $it")
}
}
然后在你的 ViewModel 中
@Keep
class ViewModelB(val savedStateHandle: SavedStateHandle): ViewModel() {
val uByteData = savedStateHandle.get<UByteArray>("id")
}
左右。 "id"
键必须与 intent extras 中的键匹配。