如何解决我的Kotlin项目中的“无法访问的代码”错误?

3pmvbmvn  于 2023-06-24  发布在  Kotlin
关注(0)|答案(1)|浏览(272)

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
    )

TokenManager.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(!错误)
无论如何,当状态发生变化时不工作?
所以第一次没有任何变化,那么它就不会工作,下面的代码就会工作,所以**activity.viewModel.refreshAccessToken(refreshToken)对吗?
如果是这种情况,逻辑是正确的,但如果navigateToMain工作,那么第一次给予Unreachable code警告是正常的,因为导航后,它转到mainActivity,这终止了splashActivity,那么
activity.viewModel.refreshAccessToken(refreshToken)**行不工作。我做的对吗?还是出了差错我不确定,所以我想问你能帮忙吗?

m1m5dgzv

m1m5dgzv1#

StateFlow永远不会完成,因此当您在state上调用collect时,collect调用下面的任何代码都不会到达,因为collect永远不会返回。
编辑:
按照您设置StateFlow的方式,当您准备导航时,它将具有complete == true值。因此,代码的快速简单修复方法是替换

viewModel.state.collect {
    activity.navigateToMain(!it.error)
}

val refreshResult = viewModel.state.first { it.completed }
activity.navigateToMain(!refreshResult.error)

first函数挂起,直到发出一个表示lambda中的条件的值,然后返回该值。
另外,在这个if (viewModel.state.value.error) {块中,您可以导航离开屏幕,但随后允许逻辑的其余部分继续。您要么需要在这个if块中调用return @launch,要么将它下面的代码 Package 在else块中。
您还需要在调用launch后删除(Dispatchers.IO)。在你的协程中没有需要它的阻塞代码,你需要在Main上在活动之间导航。
可选:这是我设计ViewModel类的方式,以避免在重新创建Fragment并再次调用refreshAccessToken()时冗余地重新启动fetch。

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
        }
    }

    //...
}

相关问题