Java 片段不';无法从ViewModel获取值

Java 片段不';无法从ViewModel获取值,java,android,kotlin,viewmodel,android-viewmodel,Java,Android,Kotlin,Viewmodel,Android Viewmodel,我有一个带有jetpack导航的单一活动应用程序,我需要一个对象变量,用于许多片段中的所有应用程序。所以我使用了一个ViewModel,我创建了一个父片段类,它提供了ViewModel: 类MyViewModel:ViewModel(){ var myData:CustomClass?=null ... } 开放类ParentFragment:Fragment{ val模型:MyViewModel by activityViewModels() lateinit var myData:Custo

我有一个带有jetpack导航的单一活动应用程序,我需要一个对象变量,用于许多片段中的所有应用程序。所以我使用了一个ViewModel,我创建了一个父片段类,它提供了ViewModel:

类MyViewModel:ViewModel(){
var myData:CustomClass?=null
...
}
开放类ParentFragment:Fragment{
val模型:MyViewModel by activityViewModels()
lateinit var myData:CustomClass
覆盖已创建的视图(视图:视图,保存状态:捆绑?){
super.onViewCreated(视图,savedInstanceState)
model.myData?让我们{
myData=it
}
}
}
myData
在我使用
ParentFragment
时不应为null,但有时,我会随机获得
kotlin.UninitializedPropertyAccessException:lateinit属性myData在我使用
myData
时未初始化

我的ViewModel是否可能没有保留
myData
?如何确保我的属性已初始化

更新:尝试1

我已在我的
父片段中尝试了此代码:

打开类ParentFragment:Fragment{
val模型:MyViewModel by activityViewModels()
lateinit var backingData:CustomClass
val myData:CustomClass
得到(){
如果(!::backingData.i初始化)
model.getData()?.let{
backingData=it
}
返回返回数据
}
覆盖已创建的视图(视图:视图,保存状态:捆绑?){
super.onViewCreated(视图,savedInstanceState)
model.getData?让我们{
backingData=it
}
}
}
但是当我调用
myData
时,问题并没有消失,似乎是
ViewModel
丢失了我的数据

更新2:更多代码详细信息

在进入扩展了
ParentFragment
片段之前,我在
ViewModel
中设置数据,然后导航到下一个
片段,如下所示:

//第一片段内部
if(myData!=null){
model.setData(myData)
findNavController().导航(FirstFragmentDirections.actionFirstToNextFragment())
}
我的
NavController
是否可能在设置数据之前进行导航

编辑3:尝试使用自定义应用程序类

根据下面的回答,我实现了一个
自定义应用程序类
,并尝试通过该类传递我的对象:

classmyapplication:Application(){
伴星{
var myObject:CustomClass?=null
}
}

但不幸的是,我没有改变。可能我的对象太大,无法正确分配?

您可以在您的属性上使用
isInitialized
进行检查。 正如文件所说:

如果此lateinit属性已指定值,则返回true,否则返回false

您可以将属性初始化为null,并使用
let
执行null检查,尽管您已经这样做了,但无需使用
lateinit
,而且要小心,它不能替代使用可为null的var

您可以这样使用:

class MyViewModel : ViewModel() {
   var mData: MutableLiveData<CustomClass>? = null
   init {
     mData = MutableLiveData<CustomClass>()
     mData!!.value =  CustomClass()
   }
   fun getData(): LiveData<CustomClass>? {
     return mData
   }
}

理想情况下,您应该将sharedVM生命周期与活动绑定,然后在所有片段中使用相同的sharedVM实例。还可以使用setter()初始化parentFragment/activity类中的myObject。然后使用getter()获取对象。 示例代码:

// SharedViewModel
var myObject : CustomClass? = null

fun setMyObject(obj : CustomClass?){
   myObject = obj
}

fun getMyObject():CustomClass?{
   return myObject
}

// Activity 
val model: SharedViewModel by viewModels()
model.setMyObject(objectValue)

// ParentFragment
private val model: SharedViewModel by activityViewModels()
val obj = model.getMyObject()
希望这对您有所帮助。快乐编码:)

试试这个:

class MyViewModel : ViewModel() {
  var myObject : CustomClass? = null
  ...
}

open class ParentFragment : Fragment {
  lateinit var model : MyViewModel by activityViewModels()


  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    model = ViewModelProvider(this).get(MyViewModel::class.java)
    if(model.myObject == null) {
        // initialize myObject, will be persisted by ViewModel
    }
  }
}
请注意,
MyViewModel
及其成员对象不应包含对
活动
片段
上下文
的任何引用,这些引用包括对
上下文
的任何间接引用,如UI视图


我不建议您使用
LiveData
(或
MutableLiveData
),因为
LiveData
的一个“特点”是它们的值是异步发布和更新的,因此调用
observe
可能太晚了。

您的措辞暗示了一些设计缺陷,即: 您将数据引用为对象变量,并使其在选择使用ViewModel时始终可访问。在我看来,你似乎考虑过度了

建议

您的对象生命周期似乎是由您自己手动管理的。因此,您应该只使用静态变量。这将转换为Kotlin作为(伴生)对象中的属性。我建议您在清单中声明一个自定义应用程序类,并在其
onCreate
-方法中分配您的对象并将其放入该类的伴生对象中。当然,您也可以在以后的任何给定时间分配它。 这将导致以下情况:

  • 始终可以通过代码中的
    应用程序.mData
    进行访问
  • 可以正确管理依赖于JVM外部实现的对象。 例如:如果您已经绑定到某个端口,则在后续调用中无法执行此操作—例如,当viewModel恢复其状态时。可能底层实现没有向Java报告错误,但分配没有成功。要实现此假设,您需要提供对象变量的描述。但作为Android世界中这种行为的一个著名例子,请尝试通过SystemServices创建一个声音池。您将体验有关此对象正确使用的信息
  • 可以在您的
    onTerminate()
    方法中完成释放 Application.class//edit_4:super.onTerminate()的Doc说系统只会杀死你的应用程序。因此,需要在您的活动中取消分配。请参阅下面的代码片段
澄清

JetPack组件的视图模型主要是res
class MyViewModel : ViewModel() {
  var myObject : CustomClass? = null
  ...
}

open class ParentFragment : Fragment {
  lateinit var model : MyViewModel by activityViewModels()


  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    model = ViewModelProvider(this).get(MyViewModel::class.java)
    if(model.myObject == null) {
        // initialize myObject, will be persisted by ViewModel
    }
  }
}
ViewModelProvider(activity!!, ViewModelFactory())[clazz]
class CustomApplication : Application() {

    companion object SharedInstances {

        /**
         *   Reference to an object accessed in various places in your application.
         *
         *   This property is initialized at a later point in time. In your case, once
         *   the user completed a required workflow in some fragment.
         *
         *   @Transient shall indicate that the state could also be not Serializable/Parcelable
         *              This _could_ require manually releasing the object.
         *              Also prohibits passing via safeArgs
         */
        @Transient var complex: CustomClass? = null
    }

}
class InitializeComplexStateFragment: Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        if (complex != null) return@onViewCreated // prohibit successive initialization.
        if (savedInstanceState != null) { /* The fragment was recreated but the object appears to be lost. */ }
        // do your heavy lifting and initialize your data at any point.
        CustomApplication.SharedInstances.complex = object : CustomClass() {
            val data = "forExampleAnSessionToken"
            /* other objects could need manual release / deallocation, like closing a fileDescriptor */
            val cObject = File("someFileDescriptorToBindTo")
        }
    }

}

class SomeOtherFragment: Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        CustomApplication.SharedInstances.complex?.let {
                // do processing
            }
            ?: propagateErrorStateInFragment()
    }

    private fun propagateErrorStateInFragment() { throw NotImplementedError("stub") }
}

class SomeActivity: Activity() {

    override fun onStop() {
        super.onStop()
        /* with multiple activities the effort increases */
        CustomApplication.complex?.close()
    }
}