Java 片段不';无法从ViewModel获取值
我有一个带有jetpack导航的单一活动应用程序,我需要一个对象变量,用于许多片段中的所有应用程序。所以我使用了一个ViewModel,我创建了一个父片段类,它提供了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
类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()
}
}