如何从可打包数据初始化 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?

如有任何提示,我们将不胜感激,谢谢。

因为你有一个实现 ParcelableViewModel,你可以直接从 Intent extra 中获取你的 ViewModelB 实例。

用于启动ActivityBIntent在实例化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 中的键匹配。