LiveData 值 returns 空
LiveData value returns null
当我想在 viewModelScope 之外接收它时,为什么我的外部不可变 LiveData 值 returns 为空?
正如你在logcat中看到的,当我想在viewModelScope中接收它或第二次调用登录功能时没有问题。
Here is my view model
class LoginViewModel(private val mainRepository: MainRepository) : ViewModel() {
private val _loginResponse = MutableLiveData<LoginResponse>()
val loginResponse: LiveData<LoginResponse> = _loginResponse
fun login(post: LoginBody) {
viewModelScope.launch {
try {
_loginResponse.value = mainRepository.loginPost(post = post)
Log.d(
LOGCAT,
"Login response : ${loginResponse.value?.data?.answerCount}"
)
} catch (e: Exception) {
Log.d(LOGCAT, "Login error: $e")
}
}
loginResponseOutsideOfTheViewModelScope()
}
fun loginResponseOutsideOfTheViewModelScope() {
Log.d(LOGCAT, "LoginResponseOutsideOfTheViewModelScope : ${loginResponse.value?.data?.answerCount}")
}
}
class LoginViewModelFactory(val app: GameApplication) :
ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
val retrofitService = app.retrofit
val context = app.applicationContext
val mainRepository = MainRepository(
retrofitService = retrofitService,
context = context
)
val viewModel = LoginViewModel(mainRepository)
return viewModel as T
}
}
Here is my Fragment where I call login function.
class FirstFragment : Fragment() {
private var _binding: FragmentFirstBinding? = null
private val binding
get() = _binding!!
private val viewModel: LoginViewModel by activityViewModels {
LoginViewModelFactory(
(activity?.application as GameApplication)
)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentFirstBinding.inflate(inflater)
binding.lifecycleOwner = this
binding.firstFragment = this
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val myPost = LoginBody(
deviceId = Settings.Secure.getString(
requireContext().contentResolver, Settings.Secure.ANDROID_ID
),
country = Locale.getDefault().country,
language = Locale.getDefault().language,
platform = Build.VERSION.SDK_INT.toString(),
version = BuildConfig.VERSION_NAME,
deviceModel = Build.MODEL
)
viewModel.login(myPost)
}
}
Logcat
2021-10-21 16:32:10.676 1520-1520/com.example.a4p1w D/DEBUG: LoginResponseOutsideOfTheViewModelScope : null
2021-10-21 16:32:12.354 1520-1520/com.example.a4p1w D/DEBUG: Login response : 12
在 login
函数中你 launch
一个异步协同程序(它获取一个值并在记录它之前将其设置在 LiveData
上),然后你记录 LiveData 的当前值.该协程需要时间,因此“当前值”日志语句首先运行(这就是它们按该顺序打印的原因)。
您的 LiveData
最初没有值(您没有将值传递给构造函数):
private val _loginResponse = MutableLiveData<LoginResponse>()
so loginResponse.value
returns null,直到你在它上面设置一个值(就像在你的协程中一样)。这就是为什么在协程完成时以及之后每次阅读它时它都能正常工作的原因
当我想在 viewModelScope 之外接收它时,为什么我的外部不可变 LiveData 值 returns 为空?
正如你在logcat中看到的,当我想在viewModelScope中接收它或第二次调用登录功能时没有问题。
Here is my view model
class LoginViewModel(private val mainRepository: MainRepository) : ViewModel() {
private val _loginResponse = MutableLiveData<LoginResponse>()
val loginResponse: LiveData<LoginResponse> = _loginResponse
fun login(post: LoginBody) {
viewModelScope.launch {
try {
_loginResponse.value = mainRepository.loginPost(post = post)
Log.d(
LOGCAT,
"Login response : ${loginResponse.value?.data?.answerCount}"
)
} catch (e: Exception) {
Log.d(LOGCAT, "Login error: $e")
}
}
loginResponseOutsideOfTheViewModelScope()
}
fun loginResponseOutsideOfTheViewModelScope() {
Log.d(LOGCAT, "LoginResponseOutsideOfTheViewModelScope : ${loginResponse.value?.data?.answerCount}")
}
}
class LoginViewModelFactory(val app: GameApplication) :
ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
val retrofitService = app.retrofit
val context = app.applicationContext
val mainRepository = MainRepository(
retrofitService = retrofitService,
context = context
)
val viewModel = LoginViewModel(mainRepository)
return viewModel as T
}
}
Here is my Fragment where I call login function.
class FirstFragment : Fragment() {
private var _binding: FragmentFirstBinding? = null
private val binding
get() = _binding!!
private val viewModel: LoginViewModel by activityViewModels {
LoginViewModelFactory(
(activity?.application as GameApplication)
)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
_binding = FragmentFirstBinding.inflate(inflater)
binding.lifecycleOwner = this
binding.firstFragment = this
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val myPost = LoginBody(
deviceId = Settings.Secure.getString(
requireContext().contentResolver, Settings.Secure.ANDROID_ID
),
country = Locale.getDefault().country,
language = Locale.getDefault().language,
platform = Build.VERSION.SDK_INT.toString(),
version = BuildConfig.VERSION_NAME,
deviceModel = Build.MODEL
)
viewModel.login(myPost)
}
}
Logcat
2021-10-21 16:32:10.676 1520-1520/com.example.a4p1w D/DEBUG: LoginResponseOutsideOfTheViewModelScope : null
2021-10-21 16:32:12.354 1520-1520/com.example.a4p1w D/DEBUG: Login response : 12
在 login
函数中你 launch
一个异步协同程序(它获取一个值并在记录它之前将其设置在 LiveData
上),然后你记录 LiveData 的当前值.该协程需要时间,因此“当前值”日志语句首先运行(这就是它们按该顺序打印的原因)。
您的 LiveData
最初没有值(您没有将值传递给构造函数):
private val _loginResponse = MutableLiveData<LoginResponse>()
so loginResponse.value
returns null,直到你在它上面设置一个值(就像在你的协程中一样)。这就是为什么在协程完成时以及之后每次阅读它时它都能正常工作的原因