Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IDLE-468] 웹소켓 연결 및 연결 실패시 재시도 로직 추가 #161

Merged
merged 2 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ android {
namespace = "com.idle.care"

defaultConfig {
versionCode = 20
versionName = "1.2.5"
versionCode = 21
versionName = "1.2.6"
targetSdk = 34

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Binary file added app/release/app-release.aab
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class ChattingRepositoryImpl @Inject constructor(
webSocketDataSource.disconnectWebSocket()

override fun subscribeChatMessage(): Flow<ChatMessage> =
webSocketDataSource.getChatMessageFlow()
webSocketDataSource.chatMessageFlow
.filterNotNull()
.map { it.toVO() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ object RetrofitModule {
fun provideWebSocketOkHttpClient(): OkHttpClient = OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(0, TimeUnit.MILLISECONDS)
.apply {
if (BuildConfig.DEBUG) {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
addInterceptor(loggingInterceptor)
}
}
.build()

@Singleton
Expand Down Expand Up @@ -122,4 +129,4 @@ annotation class AuthOkHttpClient

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class WebSocketOkHttpClient
annotation class WebSocketOkHttpClient
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@ class ChatMessageListener @Inject constructor(private val json: Json) : WebSocke
_chatMessageChannel.value = chatMessageResponse
}

override fun onOpen(webSocket: WebSocket, response: Response) {
super.onOpen(webSocket, response)
}

override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
super.onFailure(webSocket, t, response)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,47 @@
package com.idle.network.source.websocket

import com.idle.domain.model.error.ErrorHelper
import com.idle.network.BuildConfig
import com.idle.network.di.WebSocketOkHttpClient
import com.idle.network.model.chatting.ChatMessageResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.WebSocket
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.pow

@Singleton
class WebSocketDataSource @Inject constructor(
@WebSocketOkHttpClient private val client: OkHttpClient,
private val chatMessageListener: ChatMessageListener,
private val errorHelper: ErrorHelper,
) {
val chatMessageFlow: StateFlow<ChatMessageResponse?> = chatMessageListener.chatMessageFlow

private lateinit var chatMessageWebSocket: WebSocket
private var connectionAttempts = 0

fun connectWebSocket(): Result<Unit> {
return try {
suspend fun connectWebSocket(): Result<Unit> = withContext(Dispatchers.IO) {
try {
val chatMessageRequest: Request = Request.Builder()
.url(BuildConfig.CARE_WEBSOCKET_URL)
.build()

chatMessageWebSocket = client.newWebSocket(chatMessageRequest, chatMessageListener)
connectionAttempts = 0
Result.success(Unit)
} catch (e: Exception) {
Result.failure(e)
if (connectionAttempts < MAX_RETRY_ATTEMPTS) {
val waitTime = minOf(calculateBackoffTime(connectionAttempts), MAX_WAIT_TIME)
delay(waitTime)
connectionAttempts++
connectWebSocket()
} else {
Result.failure(e)
}
}
}

Expand All @@ -43,5 +56,11 @@ class WebSocketDataSource @Inject constructor(
}
}

fun getChatMessageFlow(): StateFlow<ChatMessageResponse?> = chatMessageListener.chatMessageFlow
}
private fun calculateBackoffTime(attempt: Int): Long =
(2.0.pow(attempt) * 1000).toLong()

companion object {
private const val MAX_RETRY_ATTEMPTS = 5
private const val MAX_WAIT_TIME = 10_000L // 10 seconds
}
}
20 changes: 12 additions & 8 deletions presentation/src/main/java/com/idle/presentation/MainViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.idle.presentation

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.idle.analytics.error.ErrorLoggingHelper
Expand All @@ -16,6 +17,8 @@ import com.idle.domain.model.profile.CenterManagerAccountStatus
import com.idle.domain.repositorry.jobposting.JobPostingRepository
import com.idle.domain.usecase.auth.GetAccessTokenUseCase
import com.idle.domain.usecase.auth.GetUserTypeUseCase
import com.idle.domain.usecase.chatting.ConnectWebSocketUseCase
import com.idle.domain.usecase.chatting.DisconnectWebSocketUseCase
import com.idle.domain.usecase.config.GetForceUpdateInfoUseCase
import com.idle.domain.usecase.notification.ReadNotificationUseCase
import com.idle.domain.usecase.profile.GetCenterStatusUseCase
Expand Down Expand Up @@ -47,8 +50,8 @@ class MainViewModel @Inject constructor(
private val getCenterStatusUseCase: GetCenterStatusUseCase,
private val readNotificationUseCase: ReadNotificationUseCase,
private val jobPostingRepository: JobPostingRepository,
// private val connectWebSocketUseCase: ConnectWebSocketUseCase,
// private val disconnectWebSocketUseCase: DisconnectWebSocketUseCase,
private val connectWebSocketUseCase: ConnectWebSocketUseCase,
private val disconnectWebSocketUseCase: DisconnectWebSocketUseCase,
private val errorHelper: ErrorHelper,
private val eventHelper: EventHelper,
val errorLoggingHelper: ErrorLoggingHelper,
Expand All @@ -71,15 +74,16 @@ class MainViewModel @Inject constructor(
}

internal fun connectWebSocket() = viewModelScope.launch {
// Log.d("test", "웹소켓 연결")
// connectWebSocketUseCase().onSuccess { }
// .onFailure { }
Log.d("test", "웹소켓 연결")
connectWebSocketUseCase().onFailure {
Log.d("test", it.stackTraceToString())
}
}

internal fun disconnectWebSocket() = viewModelScope.launch {
// Log.d("test", "웹소켓 연결해제")
// disconnectWebSocketUseCase().onSuccess { }
// .onFailure { }
Log.d("test", "웹소켓 연결해제")
disconnectWebSocketUseCase().onSuccess { }
.onFailure { }
}

internal fun setNavigationMenuType(navigationMenuType: NavigationMenuType) {
Expand Down