В моем приложении Android у меня есть UServiewModel, которая обрабатывает различные операции, связанные с пользователем (регистрация, вход в систему, обновление имени пользователя, изменение пароля и т. Д.). Проблема заключается в том, что после того, как один из этих методов успешно выполняется, последующие вызовы к другим методам в UserviewModel не отражают изменения в пользовательском интерфейсе или локальных данных, даже если базовый пользовательский размер успешно завершает операции (подтверждено журналами). Эта проблема влияет на все методы в USERVIEWMODEL, а не только на обновление имени пользователя.package com.kianmahmoudi.android.shirazgard.viewmodel
import android.net.Uri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.kianmahmoudi.android.shirazgard.repository.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
import com.kianmahmoudi.android.shirazgard.data.UiState
import com.parse.ParseUser
import timber.log.Timber
@HiltViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
private val _registerState = MutableLiveData(UiState.Idle)
val registerState: LiveData = _registerState
private val _loginState = MutableLiveData(UiState.Idle)
val loginState: LiveData = _loginState
private val _logoutState = MutableLiveData(UiState.Idle)
val logoutState: LiveData = _logoutState
private val _deleteAccountState = MutableLiveData(UiState.Idle)
val deleteAccountState: LiveData = _deleteAccountState
private val _passwordChangeState = MutableLiveData(UiState.Idle)
val passwordChangeState: LiveData = _passwordChangeState
private val _passwordVerificationState = MutableLiveData(UiState.Idle)
val passwordVerificationState: LiveData = _passwordVerificationState
private val _profileImageState = MutableLiveData(UiState.Idle)
val profileImageState: LiveData = _profileImageState
private val _usernameState = MutableLiveData(UiState.Idle)
val usernameState: LiveData = _usernameState
private val _profileImageDeletionState = MutableLiveData(UiState.Idle)
val profileImageDeletionState: LiveData = _profileImageDeletionState
fun registerUser(username: String, password: String) {
if (_registerState.value is UiState.Loading) return
viewModelScope.launch {
_registerState.value = UiState.Loading
runCatching {
userRepository.registerUser(username, password)
}.onSuccess {
_registerState.value = UiState.Success(it)
}.onFailure {
_registerState.value = UiState.Error(it.localizedMessage ?: "Error registering")
}
}
}
fun loginUser(username: String, password: String) {
if (_loginState.value is UiState.Loading) return
viewModelScope.launch {
_loginState.value = UiState.Loading
runCatching {
userRepository.loginUser(username, password)
}.onSuccess {
_loginState.value = UiState.Success(it)
}.onFailure {
_loginState.value = UiState.Error(it.localizedMessage ?: "Error logging in")
}
}
}
fun logout() {
if (_logoutState.value is UiState.Loading) return
viewModelScope.launch {
_logoutState.value = UiState.Loading
runCatching {
userRepository.logout()
}.onSuccess {
_logoutState.value = UiState.Success(Unit)
}.onFailure {
_logoutState.value = UiState.Error(it.localizedMessage ?: "Error logging out")
}
}
}
fun updateUsername(newUsername: String) {
if (_usernameState.value is UiState.Loading) return
viewModelScope.launch {
_usernameState.value = UiState.Loading
try {
val success = userRepository.updateUsername(newUsername)
if (success) {
// Force refresh the current user data
ParseUser.getCurrentUser()?.fetchInBackground()
_usernameState.value = UiState.Success(newUsername)
} else {
_usernameState.value = UiState.Error("Failed to update username")
}
} catch (e: Exception) {
_usernameState.value = UiState.Error(e.localizedMessage ?: "Error updating username")
}
}
}
fun uploadProfileImage(imageUri: Uri) {
if (_profileImageState.value is UiState.Loading) return
viewModelScope.launch {
_profileImageState.value = UiState.Loading
runCatching {
userRepository.uploadProfileImage(imageUri)
userRepository.getProfileImageUrl()
}.onSuccess { url ->
_profileImageState.value = UiState.Success(url)
}.onFailure {
_profileImageState.value =
UiState.Error(it.localizedMessage ?: "Image upload error")
}
}
}
fun deleteProfileImage() {
if (_profileImageDeletionState.value is UiState.Loading) return
viewModelScope.launch {
_profileImageDeletionState.value = UiState.Loading
runCatching { userRepository.deleteProfileImage() }
.onSuccess {
_profileImageDeletionState.value = UiState.Success(true)
_profileImageState.value = UiState.Success("")
}.onFailure {
_profileImageDeletionState.value =
UiState.Error(it.localizedMessage ?: "Error deleting image")
}
}
}
fun fetchProfileImageUrl() {
if (_profileImageState.value is UiState.Loading) return
viewModelScope.launch {
_profileImageState.value = UiState.Loading
runCatching { userRepository.getProfileImageUrl() }
.onSuccess {
_profileImageState.value = UiState.Success(it)
}.onFailure {
_profileImageState.value =
UiState.Error(it.localizedMessage ?: "Error fetching profile image url")
}
}
}
fun changePassword(newPassword: String) {
if (_passwordChangeState.value is UiState.Loading) return
viewModelScope.launch {
_passwordChangeState.value = UiState.Loading
runCatching { userRepository.changePassword(newPassword) }
.onSuccess {
_passwordChangeState.value = UiState.Success(true)
}.onFailure {
_passwordChangeState.value =
UiState.Error(it.localizedMessage ?: "Error changing password")
}
}
}
fun verifyCurrentPassword(password: String) {
if (_passwordVerificationState.value is UiState.Loading) return
viewModelScope.launch {
_passwordVerificationState.value = UiState.Loading
runCatching { userRepository.isCurrentPasswordCorrect(password) }
.onSuccess {
_passwordVerificationState.value = UiState.Success(it)
}.onFailure {
_passwordVerificationState.value =
UiState.Error(it.localizedMessage ?: "Password verification failed")
}
}
}
fun deleteAccount() {
if (_deleteAccountState.value is UiState.Loading) return
viewModelScope.launch {
_deleteAccountState.value = UiState.Loading
runCatching { userRepository.deleteAccount() }
.onSuccess {
_deleteAccountState.value = UiState.Success(true)
}.onFailure {
_deleteAccountState.value =
UiState.Error(it.localizedMessage ?: "Account deletion failed")
}
}
}
fun resetUsernameState() {
Timber.d("UserViewModel: resetUsernameState called")
_usernameState.postValue(UiState.Idle)
}
}
< /code>
код моего пользователя-репозиции < /p>
package com.kianmahmoudi.android.shirazgard.repository
import android.content.Context
import android.net.Uri
import com.parse.ParseCloud
import com.parse.ParseFile
import com.parse.ParseUser
import com.parse.SaveCallback
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.suspendCancellableCoroutine
import org.json.JSONObject
import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
class UserRepositoryImpl @Inject constructor(
@ApplicationContext private val context: Context
) : UserRepository {
override suspend fun registerUser(userName: String, password: String): ParseUser {
return suspendCoroutine { continuation ->
ParseUser().apply {
username = userName
setPassword(password)
signUpInBackground { e ->
if (e == null) {
continuation.resume(this)
} else {
continuation.resumeWithException(e)
}
}
}
}
}
override suspend fun loginUser(userName: String, password: String): ParseUser {
return suspendCoroutine { continuation ->
ParseUser.logInInBackground(userName, password) { user, e ->
if (e == null) {
continuation.resume(user)
} else {
continuation.resumeWithException(e)
}
}
}
}
override suspend fun changePassword(newPassword: String): Boolean {
return suspendCancellableCoroutine { continuation ->
val currentUser = ParseUser.getCurrentUser()
if (currentUser != null) {
currentUser.setPassword(newPassword)
currentUser.saveInBackground { e ->
if (e == null && continuation.isActive) {
continuation.resume(true)
} else if (continuation.isActive) {
continuation.resumeWithException(
e ?: Exception("Failed to change password")
)
}
}
} else {
continuation.resumeWithException(Exception("User not logged in"))
}
}
}
override suspend fun uploadProfileImage(imageUri: Uri): Boolean {
return suspendCoroutine { continuation ->
val user = ParseUser.getCurrentUser()
val bytes = context.contentResolver.openInputStream(imageUri)?.readBytes()
?: throw Exception("Failed to read image")
val parseFile = ParseFile("profile_${user.objectId}.jpg", bytes)
parseFile.saveInBackground(SaveCallback { e ->
if (e == null) {
user.put("profileImage", parseFile)
user.saveInBackground { e ->
if (e == null) {
continuation.resume(true)
} else {
continuation.resumeWithException(e)
}
}
} else {
continuation.resumeWithException(e)
}
})
}
}
override suspend fun getProfileImageUrl(): String {
return suspendCoroutine { continuation ->
val url = ParseUser.getCurrentUser().getParseFile("profileImage")?.url
if (url != null) {
continuation.resume(url)
} else {
continuation.resumeWithException(Exception("No profile image found"))
}
}
}
override suspend fun updateUsername(newUsername: String): Boolean {
return suspendCancellableCoroutine { continuation ->
val user = ParseUser.getCurrentUser()
if (user != null) {
user.username = newUsername
user.saveInBackground { e ->
if (e == null) {
continuation.resume(true)
} else {
continuation.resumeWithException(e)
}
}
} else {
continuation.resumeWithException(Exception("User not logged in"))
}
}
}
override suspend fun deleteProfileImage(): Boolean {
return suspendCoroutine { continuation ->
ParseUser.getCurrentUser().apply {
remove("profileImage")
saveInBackground { e ->
if (e == null) {
continuation.resume(true)
} else {
continuation.resumeWithException(e)
}
}
}
}
}
override suspend fun deleteAccount(): Boolean {
return suspendCoroutine { continuation ->
ParseUser.getCurrentUser().deleteInBackground { e ->
if (e == null) {
logout()
continuation.resume(true)
} else {
continuation.resumeWithException(e)
}
}
}
}
override fun logout() {
ParseUser.logOutInBackground()
}
override suspend fun isCurrentPasswordCorrect(password: String): Boolean {
return suspendCancellableCoroutine { continuation ->
try {
val currentUser = ParseUser.getCurrentUser()
if (currentUser != null) {
val params = hashMapOf("username" to currentUser.username, "password" to password)
ParseCloud.callFunctionInBackground("verifyPassword", params) { result, e ->
if (e == null) {
try {
val jsonObject = JSONObject(result as Map)
val success = jsonObject.getBoolean("success")
if (success) {
continuation.resume(true)
} else {
val error = jsonObject.getString("error")
continuation.resumeWithException(Exception(error))
}
} catch (jsonException: Exception) {
continuation.resumeWithException(jsonException)
}
} else {
continuation.resumeWithException(e)
}
}
} else {
continuation.resumeWithException(Exception("User not logged in"))
}
} catch (e: Exception) {
continuation.resumeWithException(Exception("Failed to verify password: ${e.localizedMessage}"))
}
}
}
}
< /code>
Одним из классов, которые возникают этой проблемой, является EditProfileFragment < /p>
Редактировать код фрагмента профиля < /p>
package com.kianmahmoudi.android.shirazgard.fragments
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.bumptech.glide.Glide
import com.kianmahmoudi.android.shirazgard.R
import com.kianmahmoudi.android.shirazgard.data.UiState
import com.kianmahmoudi.android.shirazgard.databinding.FragmentEditProfileBinding
import com.kianmahmoudi.android.shirazgard.viewmodel.UserViewModel
import com.parse.ParseUser
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import timber.log.Timber
@AndroidEntryPoint
class EditProfileFragment : Fragment(R.layout.fragment_edit_profile) {
private lateinit var binding: FragmentEditProfileBinding
private val userViewModel: UserViewModel by viewModels()
private val pickImageLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.let { uri ->
uploadProfileImage(uri)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.btnUpload.setOnClickListener { openImagePicker() }
binding.btnDelete.setOnClickListener { deleteProfileImage() }
binding.btnSave.setOnClickListener {
updateUsername()
}
viewLifecycleOwner.lifecycleScope.launch {
userViewModel.profileImageState.observe(viewLifecycleOwner) { result ->
when (result) {
is UiState.Success -> {
Glide.with(requireContext())
.load(result.data)
.placeholder(R.drawable.person_24px)
.circleCrop()
.into(binding.profileImage)
}
is UiState.Error -> {
showToast(result.message)
}
is UiState.Loading -> {
}
UiState.Idle -> {}
}
}
}
viewLifecycleOwner.lifecycleScope.launch {
userViewModel.profileImageDeletionState.observe(viewLifecycleOwner) { result ->
when (result) {
is UiState.Loading -> {
}
is UiState.Success -> {
showToast("تصویر پروفایل با موفقیت حذف شد")
binding.profileImage.setImageResource(R.drawable.person_24px)
}
is UiState.Error -> {
showToast(result.message)
}
UiState.Idle -> {}
}
}
}
viewLifecycleOwner.lifecycleScope.launch {
userViewModel.usernameState.observe(viewLifecycleOwner) { result ->
when (result) {
is UiState.Success -> {
Timber.i("Username updated to: ${ParseUser.getCurrentUser()?.username}")
Timber.i("Username: ${result.data}")
showToast("نام کاربری با موفقیت بهروزرسانی شد")
findNavController().navigateUp()
}
is UiState.Error -> {
Timber.i("Username error: ${result.message}")
showToast(result.message)
}
UiState.Loading -> {
Timber.d("EditProfileFragment: usernameState is loading")
}
UiState.Idle -> {
Timber.d("EditProfileFragment: usernameState is idle")
}
}
if (result != UiState.Idle) {
userViewModel.resetUsernameState()
}
}
}
val currentUser = ParseUser.getCurrentUser()
binding.etUsername.setText(currentUser?.username)
userViewModel.fetchProfileImageUrl()
}
private fun openImagePicker() {
val intent = Intent(Intent.ACTION_PICK).apply {
type = "image/*"
}
pickImageLauncher.launch(intent)
}
private fun uploadProfileImage(uri: Uri) {
userViewModel.uploadProfileImage(uri)
}
private fun deleteProfileImage() {
userViewModel.deleteProfileImage()
}
private fun updateUsername() {
val newUsername = binding.etUsername.text.toString().trim()
Timber.d("EditProfileFragment: updateUsername called with newUsername: $newUsername")
if (userViewModel.usernameState.value !is UiState.Loading) {
if (newUsername.isNotEmpty()) {
userViewModel.updateUsername(newUsername)
Timber.d("EditProfileFragment: userViewModel.updateUsername called")
} else {
showToast("نام کاربری نمیتواند خالی باشد")
Timber.d("EditProfileFragment: newUsername is empty")
}
} else {
Timber.d("EditProfileFragment: updateUsername is already loading")
}
}
private fun showToast(message: String) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT)
.show()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentEditProfileBinding.inflate(inflater)
return binding.root
}
}
Подробнее здесь: https://stackoverflow.com/questions/795 ... e-manageme
Методы Android ViewModel провалится после первого успешного исполнения - Управление государством или вопрос о параллелиз ⇐ Android
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение