Kotlin 协程抛出 JobCancellationException 并触发观察者
Kotlin Coroutine throws JobCancellationException and trigger observers
我正在 MVVM
架构中实现 kotlin coroutines
以替换我的 RxJava
依赖项。
我的问题:
注销后(这个细节很重要,因为我在重新开始时没有异常),我在 viewModelScope
AND[ 的启动时抛出了 JobCancellationException
协程异常=107=] 我在 LoginActivity 中的所有 observers
都被触发(wtf?)即使 try/catch
块没有执行(断点)所以它完全破坏了我的代码逻辑。
我的图层如下:
Activity
,setOnClickListener
触发,调用viewModel
函数-> ViewModel
,在viewModelScope
内创建coroutine
并调用 suspend function
-> Repository
, suspended function
从 viewModel
调用并调用 suspend function
in ApiService
-> suspend function
调用 Api
用例:
我登录了刚启动的应用程序 (LoginActivity
)。我的堆栈被清除以启动 MainActivity
。我去 SettingsActivity
注销。我注销并再次清除堆栈以开始一个全新的 LoginActivity
。当我再次尝试登录时,我抛出了 exception
并触发了我的 observers
。
登录活动:
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()
}
登录视图模型:
class LoginViewModel(private val repo: ApiRepository, private val sessionManager: SessionManager) :
ViewModel() {
private val _error = MutableLiveData<Int>()
val error: LiveData<Int>
get() = _error
private val _loadingState = MutableLiveData<LoadingState>()
val loadingState: LiveData<LoadingState>
get() = _loadingState
private val token = MutableLiveData<SessionToken>()
private val _user = MutableLiveData<User>()
val user: LiveData<User>
get() = _user
fun login(username: String, password: String) {
try {
Timber.e("enter try block outside viewModelScope.launch")
viewModelScope.launch {
try {
Timber.e("try block inside viewModelScope.launch")
_loadingState.value = LoadingState.LOADING
token.value = repo.login(username, password)
sessionManager.storeSessionToken(token.value!!.token)
sessionManager.storeUsername(username)
sessionManager.encrypt(username, password)
_user.value = repo.getUser()
_loadingState.value = LoadingState.LOADED
} catch (e: Exception) {
Timber.e("catch block inside viewModelScope.launch")
if (e is HttpException) {
when (e.code()) {
500, 503 -> {
_error.value = R.string.error_api_down
}
400 -> {
_error.value = R.string.error_bad_credentials
}
}
}
_loadingState.value = LoadingState.FAILED
}
}
}
catch (e: Exception){
Timber.e("Catch block outside viewmodelScope.launch")
when (e){
is CancellationException -> { Timber.e("CancellationException caught")}
else -> { Timber.e("Unknown caught")}
}
}
}
}
Api存储库:
class ApiRepository(
private val apiService: ApiService,
private val sessionManager: SessionManager
) {
suspend fun login(username: String, password: String): SessionToken {
val map = mutableMapOf<String, String>()
map["username"] = username
map["password"] = password
return apiService.login(
JSONObject(map.toMap()).toString()
.toRequestBody("application/json".toMediaTypeOrNull())
)
}
suspend fun getUser(): User {
return apiService.getUser(sessionManager.loadSessionToken())
}
}
Api服务:
interface ApiService {
companion object {
private const val AUTH = "Authorization"
}
@POST(ApiRouting.URL_TOKEN)
suspend fun login(@Body requestBody: RequestBody): SessionToken
@GET(ApiRouting.URL_GET_USER)
suspend fun getUser(@Header(AUTH) token: String): User
}
设置活动:
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 coroutines
的初学者,所以如果您有任何建议,link 或任何可能有帮助的欢迎。或者,如果您需要任何其他代码块,请告诉我。
编辑:
我添加了几个日志(LoginViewModel
和 MainActivity
)来详细说明当我进入我的 viewModelScope.launch
周围的 try catch 块时以及我的 MainActivity
开始时(我的 [= 之后的下一个动作) 49=] 在 LoginAvtivity
.
中触发
为了让您更清楚地了解全新(和成功)的开始是什么样的:
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
所以我们可以清楚的看到在viewModelScope.launch
里面的try
块里面没有进入,因为没有显示日志,也没有请求(profiler
空也)但是没有 Exception
被捕获在 viewModelScope.launch
周围的 try/catch
块中
确实,当我使用 breakpoint
和 step over
.
调试我的代码时,我发现了 JobCancellationException
我试图通过从 viewModelScope.launch
更改为 CoroutineScope(Dispatchers.IO).launch
来更改我的范围(只是根据问题下方的评论使用此范围进行测试)并且它崩溃了。
我终于解决了我的问题。事实上,我正在使用 Koin
作为 dependency injection
。
我只是简单地替换了 single:
val viewModelModule = module {
single { StatsViewModel(get()) }
single{ LoginViewModel(get(), get()) }
}
按视图模型:
val viewModelModule = module {
viewModel { StatsViewModel(get()) }
viewModel { LoginViewModel(get(), get()) }
}
事实上,single 是一个单例,因此通过使用 viewModel
关键字,viewModel
绑定到 Android 组件生命周期。
https://insert-koin.io/docs/reference/koin-android/viewmodel/
所以,就我而言,我使用的是 liveData 生成器,
在收到单个值后,在观察者端,我正在删除观察者,
所以在调用 remove observer 时,livedata builder 的协程被取消,抛出 JobCancellationException,所以解决方案很简单,我在获取新的实时数据时调用了 remove observer 所以对于之前的我们可以使用 remove oberserver.
我正在 MVVM
架构中实现 kotlin coroutines
以替换我的 RxJava
依赖项。
我的问题:
注销后(这个细节很重要,因为我在重新开始时没有异常),我在 viewModelScope
AND[ 的启动时抛出了 JobCancellationException
协程异常=107=] 我在 LoginActivity 中的所有 observers
都被触发(wtf?)即使 try/catch
块没有执行(断点)所以它完全破坏了我的代码逻辑。
我的图层如下:
Activity
,setOnClickListener
触发,调用viewModel
函数-> ViewModel
,在viewModelScope
内创建coroutine
并调用 suspend function
-> Repository
, suspended function
从 viewModel
调用并调用 suspend function
in ApiService
-> suspend function
调用 Api
用例:
我登录了刚启动的应用程序 (LoginActivity
)。我的堆栈被清除以启动 MainActivity
。我去 SettingsActivity
注销。我注销并再次清除堆栈以开始一个全新的 LoginActivity
。当我再次尝试登录时,我抛出了 exception
并触发了我的 observers
。
登录活动:
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()
}
登录视图模型:
class LoginViewModel(private val repo: ApiRepository, private val sessionManager: SessionManager) :
ViewModel() {
private val _error = MutableLiveData<Int>()
val error: LiveData<Int>
get() = _error
private val _loadingState = MutableLiveData<LoadingState>()
val loadingState: LiveData<LoadingState>
get() = _loadingState
private val token = MutableLiveData<SessionToken>()
private val _user = MutableLiveData<User>()
val user: LiveData<User>
get() = _user
fun login(username: String, password: String) {
try {
Timber.e("enter try block outside viewModelScope.launch")
viewModelScope.launch {
try {
Timber.e("try block inside viewModelScope.launch")
_loadingState.value = LoadingState.LOADING
token.value = repo.login(username, password)
sessionManager.storeSessionToken(token.value!!.token)
sessionManager.storeUsername(username)
sessionManager.encrypt(username, password)
_user.value = repo.getUser()
_loadingState.value = LoadingState.LOADED
} catch (e: Exception) {
Timber.e("catch block inside viewModelScope.launch")
if (e is HttpException) {
when (e.code()) {
500, 503 -> {
_error.value = R.string.error_api_down
}
400 -> {
_error.value = R.string.error_bad_credentials
}
}
}
_loadingState.value = LoadingState.FAILED
}
}
}
catch (e: Exception){
Timber.e("Catch block outside viewmodelScope.launch")
when (e){
is CancellationException -> { Timber.e("CancellationException caught")}
else -> { Timber.e("Unknown caught")}
}
}
}
}
Api存储库:
class ApiRepository(
private val apiService: ApiService,
private val sessionManager: SessionManager
) {
suspend fun login(username: String, password: String): SessionToken {
val map = mutableMapOf<String, String>()
map["username"] = username
map["password"] = password
return apiService.login(
JSONObject(map.toMap()).toString()
.toRequestBody("application/json".toMediaTypeOrNull())
)
}
suspend fun getUser(): User {
return apiService.getUser(sessionManager.loadSessionToken())
}
}
Api服务:
interface ApiService {
companion object {
private const val AUTH = "Authorization"
}
@POST(ApiRouting.URL_TOKEN)
suspend fun login(@Body requestBody: RequestBody): SessionToken
@GET(ApiRouting.URL_GET_USER)
suspend fun getUser(@Header(AUTH) token: String): User
}
设置活动:
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 coroutines
的初学者,所以如果您有任何建议,link 或任何可能有帮助的欢迎。或者,如果您需要任何其他代码块,请告诉我。
编辑:
我添加了几个日志(LoginViewModel
和 MainActivity
)来详细说明当我进入我的 viewModelScope.launch
周围的 try catch 块时以及我的 MainActivity
开始时(我的 [= 之后的下一个动作) 49=] 在 LoginAvtivity
.
为了让您更清楚地了解全新(和成功)的开始是什么样的:
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
所以我们可以清楚的看到在viewModelScope.launch
里面的try
块里面没有进入,因为没有显示日志,也没有请求(profiler
空也)但是没有 Exception
被捕获在 viewModelScope.launch
try/catch
块中
确实,当我使用 breakpoint
和 step over
.
JobCancellationException
我试图通过从 viewModelScope.launch
更改为 CoroutineScope(Dispatchers.IO).launch
来更改我的范围(只是根据问题下方的评论使用此范围进行测试)并且它崩溃了。
我终于解决了我的问题。事实上,我正在使用 Koin
作为 dependency injection
。
我只是简单地替换了 single:
val viewModelModule = module {
single { StatsViewModel(get()) }
single{ LoginViewModel(get(), get()) }
}
按视图模型:
val viewModelModule = module {
viewModel { StatsViewModel(get()) }
viewModel { LoginViewModel(get(), get()) }
}
事实上,single 是一个单例,因此通过使用 viewModel
关键字,viewModel
绑定到 Android 组件生命周期。
https://insert-koin.io/docs/reference/koin-android/viewmodel/
所以,就我而言,我使用的是 liveData 生成器, 在收到单个值后,在观察者端,我正在删除观察者, 所以在调用 remove observer 时,livedata builder 的协程被取消,抛出 JobCancellationException,所以解决方案很简单,我在获取新的实时数据时调用了 remove observer 所以对于之前的我们可以使用 remove oberserver.