Android Kotlin协同程序抛出JobCancellationException和触发器观察器

Android Kotlin协同程序抛出JobCancellationException和触发器观察器,android,kotlin,mvvm,kotlin-coroutines,android-viewmodel,Android,Kotlin,Mvvm,Kotlin Coroutines,Android Viewmodel,我正在MVVM体系结构中实现kotlin协程,以替换我的RxJava依赖关系 我的问题: 注销后(此详细信息很重要,因为我在重新开始时没有异常),在启动viewModelScope时引发了JobCancellationExceptionCorroutine异常,并且触发了LoginActivity中的所有观察者(wtf?)即使没有执行try/catch块(断点),它也会完全破坏我的代码逻辑 我的图层如下所示: 活动,设置ClickListener触发,调用视图模型函数视图模型,在视图模型范围内创

我正在
MVVM
体系结构中实现
kotlin协程
,以替换我的
RxJava
依赖关系

我的问题: 注销后(此详细信息很重要,因为我在重新开始时没有异常),在启动
viewModelScope时引发了
JobCancellationException
Corroutine异常,并且触发了LoginActivity中的所有
观察者(wtf?)即使没有执行
try/catch
块(断点),它也会完全破坏我的代码逻辑

我的图层如下所示:
活动
设置ClickListener
触发,调用
视图模型
函数
视图模型
,在
视图模型范围
内创建
协同程序
并调用
暂停函数
存储库
suspended function
viewModel
调用,并在
ApiService
->
中调用进行Api调用的
suspend function

用例: 我登录刚启动的应用程序(
LoginActivity
)。清除我的堆栈以启动
main活动
。我进入
设置活动
注销。我注销并再次清除堆栈以启动全新的
LoginActivity
。当我再次尝试登录时,会抛出
异常
,并触发我的
观察者

后勤活动:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        tvLogin.setSafeOnClickListener { login() }
}
private fun login() {
        Firebase.analytics.logEvent("hit_login", null)
        runOnUiThread {
            pDialog = SweetAlertDialog(this, SweetAlertDialog.PROGRESS_TYPE)
            pDialog!!.progressHelper.barColor = Color.parseColor("#2A6022")
            pDialog!!.titleText =
                "<font color='#2A6022'>" + resources.getString(R.string.signing) + "</font>"
            pDialog!!.setCancelable(false)
        }
        var pwd = etPassword.text.toString()
        if (passedBiometric) {
            pwd = sessionManager.decrypt(sessionManager.loadUsername())
        }
        passedBiometric = false
        viewModel.login(etUsername.text.toString(), pwd)
        viewModel.error.observe(this, {
            Toast.makeText(this, getString(it), Toast.LENGTH_SHORT).show()
        })
        viewModel.loadingState.observe(this, {
        Timber.e("LoadingState Observer triggered")
            when (it) {
                LoadingState.LOADED -> {
                    pDialog!!.dismiss()
                    Timber.e("LOADED")
                    startMainActivity()
                }
                LoadingState.FAILED -> {
                    pDialog!!.dismiss()
                    Timber.e("FAILED")
                    viewModel.error.value?.let { errorRsc ->
                        Toast.makeText(this, getString(errorRsc), Toast.LENGTH_SHORT).show()
                    }
                }
                LoadingState.LOADING -> {
                    pDialog!!.show()
                    Timber.e("LOADING")
                }
            }
        })
  }

private fun startMainActivity() {
    val intent = Intent(this, MainActivity::class.java)
    intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    intent.action = this.intent.action
    intent.putExtra(ProfileFragment.USER, viewModel.user.value)
    startActivity(intent)
    finish()
}
设置活动性:

private fun initListeners() {
    tvLogout.setSafeOnClickListener {
        DashboardManager.clearValues()
        sessionManager.clearSessionToken()
        val intent = Intent(this, LoginActivity::class.java)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        startActivity(intent)
        finish()
    }
}
TLDR:为什么会引发异常?为什么会触发观察员?为什么这种情况只发生在注销后,而不是重新开始时?

我试图在发布中添加coroutineScope,并在存储库中使用
withContext(Dispatchers.IO)
添加另一个作用域,但它没有改变任何东西

我是
kotlin协同程序的初学者,因此如果您有任何建议,欢迎提供链接或其他可能有帮助的内容。或者,如果您需要任何其他代码块,请告诉我

编辑: 我添加了几个日志(
LoginViewModel
MainActivity
),以详细说明我在
viewModelScope.launch周围的try-catch块中输入时以及在
loginActivity
中触发我的
MainActivity
启动时(my
observer
之后的下一个操作)

要让您更清楚地了解全新(成功)的开始,请执行以下操作:

E/LoginViewModel: enter try block outside viewModelScope.launch
E/LoginViewModel$login: try block inside viewModelScope.launch
E/LoginActivity$login: LoadingState Observer triggered
V/ViewRootImpl: The specified message queue synchronization  barrier token has not been posted or has already been removed
E/LoginActivity$login: LOADING
    LoadingState Observer triggered
    LOADING
I/okhttp.OkHttpClient: --> POST http://45.153.184.141/api-token-auth/
    Content-Type: application/json; charset=utf-8
    Content-Length: 46
    --> END POST (46-byte body)
E/LoginActivity$login: LoadingState Observer triggered
    LOADING
D/DecorView: onWindowFocusChangedFromViewRoot hasFocus: true, DecorView@7175d32[LoginActivity]
W/Choreographer: Already have a pending vsync event.  There should only be one at a time.
V/ViewRootImpl: The specified message queue synchronization  barrier token has not been posted or has already been removed
W/Choreographer: Already have a pending vsync event.  There should only be one at a time.
I/okhttp.OkHttpClient: <-- 200 OK http://xx.xx.xx.xx/api-token-auth/ (237ms)
    Date: Wed, 03 Mar 2021 21:31:39 GMT
    Server: WSGIServer/0.2 CPython/3.8.2
    Content-Type: application/json
    Allow: POST, OPTIONS
    X-Frame-Options: DENY
    Content-Length: 52
    Vary: Cookie
    X-Content-Type-Options: nosniff
    <-- END HTTP (52-byte body)
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object; (greylist, linking, allowed)
    Accessing hidden method Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V (greylist, linking, allowed)
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object; (greylist, linking, allowed)
    Accessing hidden method Lsun/misc/Unsafe;->putInt(Ljava/lang/Object;JI)V (greylist, linking, allowed)
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J (greylist,core-platform-api, linking, allowed)
    Accessing hidden method Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J (greylist,core-platform-api, linking, allowed)
D/TransportRuntime.SQLiteEventStore: Storing event with priority=DEFAULT, name=FIREPERF for destination cct
D/TransportRuntime.JobInfoScheduler: Scheduling upload for context TransportContext(cct, DEFAULT, MSRodHRwczovL2ZpcmViYXNlbG9nZ2luZy1wYS5nb29nbGVhcGlzLmNvbS92MS9maXJlbG9nL2xlZ2FjeS9iYXRjaGxvZ1xBSXphU3lDY2traUg4aTJaQVJ3T3MxTEV6RktsZDE1YU9HOG96S28=) with jobId=877081683 in 30000ms(Backend next call timestamp 1614807010307). Attempt 1
I/okhttp.OkHttpClient: --> GET http://45.153.184.141/api/users/profile/
    Authorization: Token token
    --> END GET
D/OnePlusJankManager:  Chor uploadMDM JANK_TYPE_ONCE mViewTitle = com.weedmanager.myweedmanager/com.weedmanager.myweedmanager.authentication.LoginActivity--- jank level = 1
I/okhttp.OkHttpClient: <-- 200 OK http://45.153.184.141/api/users/profile/ (83ms)
    Date: Wed, 03 Mar 2021 21:31:40 GMT
    Server: WSGIServer/0.2 CPython/3.8.2
    Content-Type: application/json
    Vary: Accept
    Allow: GET, HEAD, OPTIONS
    X-Frame-Options: DENY
    Content-Length: 103
    X-Content-Type-Options: nosniff
    <-- END HTTP (103-byte body)
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V (greylist, linking, allowed)
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->getInt(Ljava/lang/Object;J)I (greylist, linking, allowed)
    Accessing hidden method Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object; (greylist, linking, allowed)
E/LoginActivity$login: LoadingState Observer triggered
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J (greylist,core-platform-api, linking, allowed)
    Accessing hidden method Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J (greylist,core-platform-api, linking, allowed)
E/LoginActivity$login: LOADED
D/TransportRuntime.SQLiteEventStore: Storing event with priority=DEFAULT, name=FIREPERF for destination cct
D/DecorView: onWindowFocusChangedFromViewRoot hasFocus: true, DecorView@4a8ec73[LoginActivity]
D/TransportRuntime.JobInfoScheduler: Upload for context TransportContext(cct, DEFAULT, MSRodHRwczovL2ZpcmViYXNlbG9nZ2luZy1wYS5nb29nbGVhcGlzLmNvbS92MS9maXJlbG9nL2xlZ2FjeS9iYXRjaGxvZ1xBSXphU3lDY2traUg4aTJaQVJ3T3MxTEV6RktsZDE1YU9HOG96S28=) is already scheduled. Returning...
E/LoginActivity$login: LoadingState Observer triggered
    LOADED
E/LoginActivity$login: LoadingState Observer triggered
    LOADED
D/OnePlusJankManager:  Chor uploadMDM JANK_TYPE_ONCE mViewTitle = com.weedmanager.myweedmanager/com.weedmanager.myweedmanager.authentication.LoginActivity--- jank level = 1
V/FA: Recording user engagement, ms: 1494
W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@c6ecc8e
V/FA: Activity paused, time: 1057027342
D/FA: Event not sent since app measurement is disabled
V/FA: onActivityCreated
E/MainActivity: MainActivity onCreate
因此,我们可以清楚地看到,它没有进入
viewModelScope.launch中的
try
块,因为日志没有显示,请求也没有发出(
profiler
也为空),但在
try/catch
块中没有捕获到
viewModelScope.launch

事实上,当我使用
断点和
单步调试代码时,我发现了
JobCancellationException


我试图通过从
viewModelScope.launch
更改为
CoroutineScope(Dispatchers.IO)。launch
(只是根据问题下面的注释使用此范围进行测试)来更改我的范围,结果它崩溃了。

我终于解决了我的问题。实际上,我正在使用
Koin
进行
依赖项注入。
我只是简单地取代了单身:

val viewModelModule = module {
    single { StatsViewModel(get()) }
    single{ LoginViewModel(get(), get()) }
}
按视图模型:

val viewModelModule = module {
    viewModel { StatsViewModel(get()) }
    viewModel { LoginViewModel(get(), get()) }
}
事实上,single是singleton,因此通过使用
viewModel
关键字,将
viewModel
绑定到Android组件生命周期


在viewModelScope.launch之后立即调用coroutineScope有什么原因吗?我认为您不需要它。您可以改为尝试使用supervisorScope而不是coroutineScope。在coroutines中错误处理是一个正常的流程,因此作业取消可能来自您的尝试,而您没有处理它,因此失败了。乍一看,我没有看到任何其他可能的取消。朱塞佩是对的,在发布后不需要使用coroutineScope,尽管我不认为其他作用域可以解决这个问题。@Giuseppegiacopo我忘了删除它,这确实是一个测试,看看它是否会以任何方式影响异常…我删除了coroutineScope,另外,这是一个错误测试。你知道如何处理作业取消吗?我试着启动viewmodel.login()在try/catch块中,捕获未被触发…@BenjaminCharaisIt似乎未被触发,因为协程的上下文被提升到了开始的位置,这是您得到错误的地方。因此,您需要在启动时进行try/catch,这是一个错误。您永远不会在失败时进行单次
查看模式
l这是拥有
ViewModel
的全部目的。很高兴你自己找到了它。事实上,这是我第一次使用Koin,所以我不知道ViewModel关键字。。
E/LoginViewModel: enter try block outside viewModelScope.launch
E/LoginViewModel$login: try block inside viewModelScope.launch
E/LoginActivity$login: LoadingState Observer triggered
V/ViewRootImpl: The specified message queue synchronization  barrier token has not been posted or has already been removed
E/LoginActivity$login: LOADING
    LoadingState Observer triggered
    LOADING
I/okhttp.OkHttpClient: --> POST http://45.153.184.141/api-token-auth/
    Content-Type: application/json; charset=utf-8
    Content-Length: 46
    --> END POST (46-byte body)
E/LoginActivity$login: LoadingState Observer triggered
    LOADING
D/DecorView: onWindowFocusChangedFromViewRoot hasFocus: true, DecorView@7175d32[LoginActivity]
W/Choreographer: Already have a pending vsync event.  There should only be one at a time.
V/ViewRootImpl: The specified message queue synchronization  barrier token has not been posted or has already been removed
W/Choreographer: Already have a pending vsync event.  There should only be one at a time.
I/okhttp.OkHttpClient: <-- 200 OK http://xx.xx.xx.xx/api-token-auth/ (237ms)
    Date: Wed, 03 Mar 2021 21:31:39 GMT
    Server: WSGIServer/0.2 CPython/3.8.2
    Content-Type: application/json
    Allow: POST, OPTIONS
    X-Frame-Options: DENY
    Content-Length: 52
    Vary: Cookie
    X-Content-Type-Options: nosniff
    <-- END HTTP (52-byte body)
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object; (greylist, linking, allowed)
    Accessing hidden method Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V (greylist, linking, allowed)
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object; (greylist, linking, allowed)
    Accessing hidden method Lsun/misc/Unsafe;->putInt(Ljava/lang/Object;JI)V (greylist, linking, allowed)
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J (greylist,core-platform-api, linking, allowed)
    Accessing hidden method Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J (greylist,core-platform-api, linking, allowed)
D/TransportRuntime.SQLiteEventStore: Storing event with priority=DEFAULT, name=FIREPERF for destination cct
D/TransportRuntime.JobInfoScheduler: Scheduling upload for context TransportContext(cct, DEFAULT, MSRodHRwczovL2ZpcmViYXNlbG9nZ2luZy1wYS5nb29nbGVhcGlzLmNvbS92MS9maXJlbG9nL2xlZ2FjeS9iYXRjaGxvZ1xBSXphU3lDY2traUg4aTJaQVJ3T3MxTEV6RktsZDE1YU9HOG96S28=) with jobId=877081683 in 30000ms(Backend next call timestamp 1614807010307). Attempt 1
I/okhttp.OkHttpClient: --> GET http://45.153.184.141/api/users/profile/
    Authorization: Token token
    --> END GET
D/OnePlusJankManager:  Chor uploadMDM JANK_TYPE_ONCE mViewTitle = com.weedmanager.myweedmanager/com.weedmanager.myweedmanager.authentication.LoginActivity--- jank level = 1
I/okhttp.OkHttpClient: <-- 200 OK http://45.153.184.141/api/users/profile/ (83ms)
    Date: Wed, 03 Mar 2021 21:31:40 GMT
    Server: WSGIServer/0.2 CPython/3.8.2
    Content-Type: application/json
    Vary: Accept
    Allow: GET, HEAD, OPTIONS
    X-Frame-Options: DENY
    Content-Length: 103
    X-Content-Type-Options: nosniff
    <-- END HTTP (103-byte body)
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V (greylist, linking, allowed)
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->getInt(Ljava/lang/Object;J)I (greylist, linking, allowed)
    Accessing hidden method Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object; (greylist, linking, allowed)
E/LoginActivity$login: LoadingState Observer triggered
W/r.myweedmanage: Accessing hidden method Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J (greylist,core-platform-api, linking, allowed)
    Accessing hidden method Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J (greylist,core-platform-api, linking, allowed)
E/LoginActivity$login: LOADED
D/TransportRuntime.SQLiteEventStore: Storing event with priority=DEFAULT, name=FIREPERF for destination cct
D/DecorView: onWindowFocusChangedFromViewRoot hasFocus: true, DecorView@4a8ec73[LoginActivity]
D/TransportRuntime.JobInfoScheduler: Upload for context TransportContext(cct, DEFAULT, MSRodHRwczovL2ZpcmViYXNlbG9nZ2luZy1wYS5nb29nbGVhcGlzLmNvbS92MS9maXJlbG9nL2xlZ2FjeS9iYXRjaGxvZ1xBSXphU3lDY2traUg4aTJaQVJ3T3MxTEV6RktsZDE1YU9HOG96S28=) is already scheduled. Returning...
E/LoginActivity$login: LoadingState Observer triggered
    LOADED
E/LoginActivity$login: LoadingState Observer triggered
    LOADED
D/OnePlusJankManager:  Chor uploadMDM JANK_TYPE_ONCE mViewTitle = com.weedmanager.myweedmanager/com.weedmanager.myweedmanager.authentication.LoginActivity--- jank level = 1
V/FA: Recording user engagement, ms: 1494
W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@c6ecc8e
V/FA: Activity paused, time: 1057027342
D/FA: Event not sent since app measurement is disabled
V/FA: onActivityCreated
E/MainActivity: MainActivity onCreate
E/LoginViewModel: enter try block outside viewModelScope.launch
D/FA: Event not sent since app measurement is disabled
E/LoginActivity$login: LoadingState Observer triggered
E/LoginActivity$login: LOADED
V/ViewRootImpl: The specified message queue synchronization  barrier token has not been posted or has already been removed
V/FA: Recording user engagement, ms: 8496
V/FA: Connecting to remote service
V/FA: Activity paused, time: 1057275561
W/Choreographer: Already have a pending vsync event.  There should only be one at a time.
D/FA: Event not sent since app measurement is disabled
W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@24faad3
E/MainActivity: MainActivity onCreate
val viewModelModule = module {
    single { StatsViewModel(get()) }
    single{ LoginViewModel(get(), get()) }
}
val viewModelModule = module {
    viewModel { StatsViewModel(get()) }
    viewModel { LoginViewModel(get(), get()) }
}