【问题标题】:Android screen rotation during API call gives kotlinx.coroutines.JobCancellationExceptionAPI 调用期间的 Android 屏幕旋转会产生 kotlinx.coroutines.JobCancellationException
【发布时间】:2021-03-31 07:52:23
【问题描述】:

我收到此错误 -

System.err: kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}

这是我的片段代码 -

@AndroidEntryPoint
class LoginView : Fragment(R.layout.login_view) {

    private val viewModel by viewModels<LoginViewModel>()

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

        view.login_button.setOnClickListener {
            val email = view.email_input_layout.text.toString()
            val password = view.password_input_layout.text.toString()
        
            viewModel.loginUser(email, password).observe(viewLifecycleOwner, {
                // ….
            })
        }
    }
}

这是我的 ViewModel 代码 -

fun loginUser(email: String, password: String)= repo.loginUser(Action.LoginUser(email, password)).map {
    when (it) {
       // …                    
     }
 }.asLiveData(Dispatchers.Default + viewModelScope.coroutineContext)

这里是回购代码 -

fun loginUser(action: Action) = flow {
  when (action) {
     is Action.LoginUser -> {
            emit(Result.Loading)
            val result = remoteSource.loginUser(LoginModel(action.email, action.password))
            emit(result)
     }
  }
}

这里是远程数据源代码-

override suspend fun loginUser(loginModel: LoginModel): Result {
    try {
         Log.e(TAG, “Going to do api call" + Thread.currentThread().name)
         val result = withContext(Dispatchers.IO) {
                  Log.e(TAG, “Doing api call" + Thread.currentThread().name)
                  apiInterface.loginUser(loginModel)
         }
         if (result.isSuccessful) {
            Log.e(TAG, “Api call success")
            return Result.Success(result.body())
         } else {
            Log.e(TAG, “Api call error”)
            return Result.Error(result.code())
         }
     } catch (e: Exception) {
       // Here I catch the JobCancellationException
        e.printStackTrace()
     }
     return Result.Error(UNKNOWN_ERROR)
}

最后是 Api 接口 -

 @POST("login")
 suspend fun loginUser(@Body loginModel: LoginModel): Response<LoginResponseModel>

我模拟了要在 20 秒内交付的 API 响应。

问题来了-

我点击了登录按钮,登录调用正在发生。现在我看到了日志 -

Going to do api call main

Doing api call DefaultDispatcher-worker-1

如您所见,我一直在 main 线程上,直到请求调用并且在我进行调用时发生线程切换。

现在我在 API 调用中间旋转手机,我的视图是预期的新视图,我没有看到任何日志,几秒钟内我得到了 JobCancellationException,我在 RemoteDataSource 中捕获.谁能指导我这里到底是什么错误

【问题讨论】:

  • 您是在扩展 Android ViewModel 类还是拥有自定义视图模型?
  • 我的虚拟机看起来像这样 - class LoginViewModel @ViewModelInject constructor(private val repo: LoginRepository) : ViewModel() { ... } 所以我扩展了 androidx.lifecycle.ViewModel
  • 你的viewModel是怎么创建的,是@Inject lateinit var vm: MyViewModel做的吗?
  • @EpicPandaForce 它由 viewModels() 在片段中创建为私有 val viewModel,其中 viewModels 是 androidx.fragment.app.viewModels。
  • @EpicPandaForce 我也用片段代码更新了我的问题

标签: android kotlin android-livedata kotlin-coroutines


【解决方案1】:

好的,我解决了这个问题 -

这是我的 ViewModel -

var loginViewState: LiveData<LoginViewState> = MutableLiveData()

fun loginUser(email: String, password: String) {
         loginViewState = repo.loginUser(Action.LoginUser(email, password)).map {
                    // Do the mapping
                }.asLiveData(Dispatchers.Default + viewModelScope.coroutineContext)
}

我的 Fragment 看起来像这样 -

@AndroidEntryPoint
class LoginView : Fragment(R.layout.login_view) {

    private val viewModel by viewModels<LoginViewModel>()

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

        view.login_button.setOnClickListener {
            val email = view.email_input_layout.text.toString()
            val password = view.password_input_layout.text.toString()
            viewModel.loginUser(email, password)  
            viewModel.loginViewState.observe(viewLifecycleOwner, {
               // …
            })      
        }

        viewModel.loginViewState.observe(viewLifecycleOwner, {
               // …
        })
    }
}

所以解决方案是使用一个变量并观察它的变化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-28
    • 2013-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-12
    相关资源
    最近更新 更多