提问人:NewPartizal 提问时间:6/8/2023 最后编辑:NewPartizal 更新时间:6/9/2023 访问量:150
如何解决 kotlin 项目中的“无法访问代码”错误?
How can I solve "Unreachable code" error in my kotlin project?
问:
SplashActivity.kt (英语)
@AndroidEntryPoint
@SuppressLint("CustomSplashScreen")
class SplashActivity : ComponentActivity() {
private val viewModel: SplashScreenViewModel by viewModels()
private val tokenManager = TokenManager(this)
val activity = this
private val errMsg = "Bir hata ile karşılaşıldı."
private fun navigateToMain(isTokenExist: Boolean) {
val intent = Intent(this@SplashActivity, MainActivity::class.java)
intent.putExtra("isTokenExist", isTokenExist)
startActivity(intent)
finish()
}
override fun onCreate(savedInstanceState: Bundle?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { true }
}
super.onCreate(savedInstanceState)
if (viewModel.state.value.error) {
Toast.makeText(this@SplashActivity, errMsg, Toast.LENGTH_LONG).show()
}
lifecycleScope.launch(Dispatchers.IO) {
tokenManager.getRefreshToken().collect { refreshToken ->
if (refreshToken != null) {
if (viewModel.state.value.error) {
delay(2000)
activity.navigateToMain(false)
}
viewModel.state.collect {
activity.navigateToMain(!it.error)
}
activity.viewModel.refreshAccessToken(refreshToken) //unreachable code warning
} else {
delay(2000)
activity.navigateToMain(false)
}
}
}
}
}
SplashScreenViewModel.kt
@HiltViewModel
class SplashScreenViewModel @Inject constructor(
private val tokenManager: TokenManager
) : ViewModel() {
private val _state = MutableStateFlow(SplashScreenState())
val state: StateFlow<SplashScreenState> = _state.asStateFlow()
fun refreshAccessToken(refreshToken: String) {
viewModelScope.launch {
try {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
val okHttpClient = OkHttpClient
.Builder()
.addInterceptor(loggingInterceptor)
//.authenticator(AuthAuthenticator(tokenManager))
.build()
val retrofit = Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
val service = retrofit.create(RegisterService::class.java)
val response = service.refreshToken(model = RefreshToken(refreshToken))
if (response.isSuccessful && response.body() != null) {
val newAccessToken = response.body()?.access_token
val newRefreshToken = response.body()?.refresh_token
if (newAccessToken != null && newRefreshToken != null) {
tokenManager.saveAccessToken(newAccessToken)
tokenManager.saveRefreshToken(newRefreshToken)
}
}
_state.update {
it.copy(
completed = true
)
}
} catch (e: Exception) {
_state.update {
it.copy(
error = true,
completed = true
)
}
}
}
}
}
data class SplashScreenState(
val error: Boolean = false,
var completed:Boolean = false
)
令牌管理器.kt
class TokenManager(private val context: Context) {
companion object {
private val ACCESS_TOKEN = stringPreferencesKey("access_token")
private val REFRESH_TOKEN = stringPreferencesKey("refresh_token")
private val EXPIRATION_TIME = longPreferencesKey("expiration_time")
}
fun getAccessToken(): Flow<String?> {
return context.dataStore.data.map { preferences ->
preferences[ACCESS_TOKEN]
}
}
suspend fun saveAccessToken(token: String) {
context.dataStore.edit { preferences ->
preferences[ACCESS_TOKEN] = token
val expirationTime = System.currentTimeMillis()
preferences[EXPIRATION_TIME] = expirationTime + 86400 * 1000
}
}
fun getAccessTokenExpirationTime(): Flow<Long?> {
return context.dataStore.data.map { preferences ->
preferences[EXPIRATION_TIME]
}
}
suspend fun deleteAccessToken() {
context.dataStore.edit { preferences ->
preferences.remove(ACCESS_TOKEN)
}
}
fun getRefreshToken(): Flow<String?> {
return context.dataStore.data.map { preferences ->
preferences[REFRESH_TOKEN]
}
}
suspend fun saveRefreshToken(token: String) {
context.dataStore.edit { preferences ->
preferences[REFRESH_TOKEN] = token
val expirationTime = System.currentTimeMillis()
preferences[EXPIRATION_TIME] = expirationTime + 86400 * 1000
}
}
suspend fun deleteRefreshToken() {
context.dataStore.edit { preferences ->
preferences.remove(REFRESH_TOKEN)
}
}
我收到“无法访问”代码警告。我在上面分享了我的启动活动和 viewodel 代码。我认为我制作的代码是正确的,但是有一部分我不喜欢,为什么android studio会发出这个警告。
viewModel.state.collect {
activity.navigateToMain(!it.error)
}
activity.viewModel.refreshAccessToken(refreshToken) // --> Unreachable code warning
在这行代码中,activity.navigateToMain(!it.error)
无论如何,当状态发生变化时不起作用?
所以第一次没有变化,那么它就不起作用了,下面的代码就可以工作了 所以 activity.viewModel.refreshAccessToken(refreshToken) 对吧?
如果是这种情况,逻辑是正确的,但如果 navigateToMain 有效,那么第一次发出 Unreachable 代码警告是正常的,因为在导航后,它会转到 mainActivity 并终止 splashActivity,然后 activity.viewModel.refreshAccessToken(refreshToken) 行不起作用。我所做的是对的吗?还是有错误?我不确定,所以我想问你能帮忙吗?
答:
StateFlow 永远不会完成,因此当您调用 时,调用下面的任何代码都不会被访问,因为永远不会返回。collect
state
collect
collect
编辑:
按照 StateFlow 的设置方式,当您准备好导航时,它将具有一个值。因此,代码的快速简便的解决方法是将complete == true
viewModel.state.collect {
activity.navigateToMain(!it.error)
}
跟
val refreshResult = viewModel.state.first { it.completed }
activity.navigateToMain(!refreshResult.error)
该函数将挂起,直到发出一个值,该值表示 lambda 中的条件,然后返回该值。first
此外,将此块放在您离开屏幕的位置,但随后允许其余逻辑继续。你要么需要在这个块内部调用,要么将它下面的代码包装在一个块中。if (viewModel.state.value.error) {
return @launch
if
else
您还需要在通话后删除。协程中没有需要它的阻塞代码,您需要在活动之间导航。(Dispatchers.IO)
launch
Main
可选:这就是我设计 ViewModel 类的方式,以避免在重新创建 Fragment 并再次调用时冗余地重新启动提取的可能性。refreshAccessToken()
class SplashScreenViewModel @Inject constructor(
private val tokenManager: TokenManager
) : ViewModel() {
private val _state = MutableStateFlow(SplashScreenState())
val state: StateFlow<SplashScreenState> = _state.asStateFlow()
private val refreshJob: Job? = null
fun refreshAccessToken(refreshToken: String) {
if (refreshJob != null) {
return
}
refreshJob = viewModelScope.launch {
// your original code
// After all code from your original coroutine, if you want to
// support ability to call this function again to do another refresh:
refreshJob = null
}
}
//...
}
评论