Skip to content

Commit

Permalink
Add Auto Connection Feature using Application.ActivityLifecycleCallbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
jaeyunn15 committed Oct 7, 2023
1 parent 5aa7914 commit 9af7c17
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion thunder-core/.gitignore

This file was deleted.

59 changes: 0 additions & 59 deletions thunder-core/build.gradle

This file was deleted.

Empty file removed thunder-core/consumer-rules.pro
Empty file.
21 changes: 0 additions & 21 deletions thunder-core/proguard-rules.pro

This file was deleted.

4 changes: 0 additions & 4 deletions thunder-core/src/main/AndroidManifest.xml

This file was deleted.

5 changes: 5 additions & 0 deletions thunder/src/main/java/com/jeremy/thunder/Extension.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.jeremy.thunder

import android.util.Log
import java.lang.reflect.Array
import java.lang.reflect.GenericArrayType
import java.lang.reflect.ParameterizedType
Expand Down Expand Up @@ -56,4 +57,8 @@ inline fun thunder(init: Thunder.Builder.() -> Unit): Thunder {
val thunder = Thunder.Builder()
thunder.init()
return thunder.build()
}

internal fun thunderLog(value: String) {
Log.d("⚡️Thunder⚡️ :: ", value)
}
22 changes: 16 additions & 6 deletions thunder/src/main/java/com/jeremy/thunder/Thunder.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.jeremy.thunder

import android.app.Application
import android.content.Context
import com.jeremy.thunder.coroutine.CoroutineScope.scope
import com.jeremy.thunder.cache.CacheController
import com.jeremy.thunder.connection.AppConnectionProvider
import com.jeremy.thunder.coroutine.CoroutineScope.scope
import com.jeremy.thunder.event.EventProcessor
import com.jeremy.thunder.event.WebSocketEvent
import com.jeremy.thunder.event.WebSocketEventProcessor
Expand Down Expand Up @@ -52,16 +54,21 @@ class Thunder private constructor(
private var webSocketCore: WebSocket.Factory? = null
private var thunderStateManager: ThunderStateManager? = null
private var context: Context? = null
private val appConnectionProvider by lazy { AppConnectionProvider() }

fun webSocketCore(core: WebSocket.Factory): Builder = apply { this.webSocketCore = core }

fun setApplicationContext(context: Context): Builder = apply { this.context = context }
fun setApplicationContext(context: Context): Builder = apply {
this.context = context
(this.context as Application).registerActivityLifecycleCallbacks(appConnectionProvider)
}

private fun createThunderStateManager(): ThunderStateManager {
thunderStateManager = ThunderStateManager.Factory(
createNetworkConnectivity(),
createCacheController(),
checkNotNull(webSocketCore)
connectionListener = appConnectionProvider,
networkStatus = createNetworkConnectivity(),
cacheController = createCacheController(),
webSocketCore = checkNotNull(webSocketCore)
).create()
return checkNotNull(thunderStateManager)
}
Expand Down Expand Up @@ -90,7 +97,10 @@ class Thunder private constructor(
}

private fun createServiceExecutor(): ServiceExecutor {
return ServiceExecutor.Factory(createThunderProvider(), scope).create()
return ServiceExecutor.Factory(
thunderProvider = createThunderProvider(),
scope = scope
).create()
}

fun build(): Thunder {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.jeremy.thunder.connection

import com.jeremy.thunder.state.ActivityState
import kotlinx.coroutines.flow.StateFlow

interface AppConnectionListener {
fun collectState(): StateFlow<ActivityState>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.jeremy.thunder.connection

import android.app.Activity
import android.app.Application
import android.os.Bundle
import com.jeremy.thunder.state.ActivityState
import com.jeremy.thunder.state.GetReady
import com.jeremy.thunder.state.Initialize
import com.jeremy.thunder.state.ShutDown
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class AppConnectionProvider : AppConnectionListener, Application.ActivityLifecycleCallbacks {

private val _eventFlow = MutableStateFlow<ActivityState>(Initialize)

override fun collectState(): StateFlow<ActivityState> {
return _eventFlow.asStateFlow()
}

override fun onActivityCreated(p0: Activity, p1: Bundle?) {
_eventFlow.tryEmit(GetReady)
}

override fun onActivityStarted(p0: Activity) {
_eventFlow.tryEmit(GetReady)
}

override fun onActivityResumed(p0: Activity) {
_eventFlow.tryEmit(GetReady)
}

override fun onActivityPaused(p0: Activity) {}

override fun onActivityStopped(p0: Activity) {}

override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {}

override fun onActivityDestroyed(p0: Activity) {
_eventFlow.tryEmit(ShutDown)
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.jeremy.thunder.internal

import com.jeremy.thunder.coroutine.CoroutineScope.scope
import com.jeremy.thunder.state.NetworkState
import com.jeremy.thunder.state.ThunderError
import com.jeremy.thunder.state.ThunderState
import com.jeremy.thunder.ws.WebSocket
import com.jeremy.thunder.cache.CacheController
import com.jeremy.thunder.cache.RecoveryCache
import com.jeremy.thunder.cache.ValveCache
import com.jeremy.thunder.connection.AppConnectionListener
import com.jeremy.thunder.coroutine.CoroutineScope.scope
import com.jeremy.thunder.event.WebSocketEvent
import com.jeremy.thunder.network.NetworkConnectivityService
import com.jeremy.thunder.state.GetReady
import com.jeremy.thunder.state.Initialize
import com.jeremy.thunder.state.NetworkState
import com.jeremy.thunder.state.ShutDown
import com.jeremy.thunder.state.ThunderError
import com.jeremy.thunder.state.ThunderState
import com.jeremy.thunder.ws.WebSocket
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
Expand All @@ -21,20 +25,20 @@ import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

/*
* Manage ThunderState as SocketState using NetworkState
* ThunderState에 따라 Socket Lifecycle Manage
*
/**
* Manage ThunderState as SocketState using NetworkState
*
* */

class ThunderStateManager private constructor(
connectionListener: AppConnectionListener,
networkState: NetworkConnectivityService,
private val recoveryCache: RecoveryCache,
private val valveCache: ValveCache,
private val webSocketCore: WebSocket.Factory,
private val scope: CoroutineScope
) {
lateinit var socket: WebSocket
private var socket: WebSocket? = null

private val _socketState = MutableStateFlow<ThunderState>(ThunderState.IDLE)

Expand All @@ -53,6 +57,18 @@ class ThunderStateManager private constructor(
valveCache.onUpdateValveState(it)
}.launchIn(scope)

connectionListener.collectState().onEach {
when(it) {
GetReady -> {
openConnection()
}
ShutDown -> {
closeConnection()
}
Initialize -> Unit
}
}.launchIn(scope)

networkState.networkStatus.onEach {
when (it) {
NetworkState.Available -> {
Expand Down Expand Up @@ -85,16 +101,10 @@ class ThunderStateManager private constructor(
}
}.launchIn(scope)

/*
* requestFlow로 요청 사항이 흘러 들어오면 우선적으로 ValveControl에 보냄
* valve 활성화에 따라 다시 흘려보냄.
* */

combine(
_socketState,
valveCache.emissionOfValveFlow()
) { currentState, request ->

when (currentState) {
ThunderState.IDLE -> Unit
ThunderState.CONNECTING -> {
Expand Down Expand Up @@ -124,25 +134,29 @@ class ThunderStateManager private constructor(
}.launchIn(scope)
}

private fun requestSendMessage(message: String) {
if (socket.send(message)) {
private fun requestSendMessage(message: String) = socket?.let{
if (it.send(message)) {
recoveryCache.set(message)
}
}

private lateinit var connectionJob: Job

private fun openConnection() {
socket = webSocketCore.create()
socket.open()
if (::connectionJob.isInitialized) connectionJob.cancel()
connectionJob = socket.events().onEach { _events.tryEmit(it) }.launchIn(scope)
if (socket == null) {
socket = webSocketCore.create()
socket?.let { webSocket ->
webSocket.open()
if (::connectionJob.isInitialized) connectionJob.cancel()
connectionJob = webSocket.events().onEach { _events.tryEmit(it) }.launchIn(scope)
}
}
}

private fun closeConnection() {
if (::socket.isInitialized) {
socket.close(1000, "shutdown")
socket?.let {
it.close(1000, "shutdown")
if (::connectionJob.isInitialized) connectionJob.cancel()
socket = null
}
}

Expand All @@ -155,12 +169,14 @@ class ThunderStateManager private constructor(
}

class Factory(
private val connectionListener: AppConnectionListener,
private val networkStatus: NetworkConnectivityService,
private val cacheController: CacheController,
private val webSocketCore: WebSocket.Factory
) {
fun create(): ThunderStateManager {
return ThunderStateManager(
connectionListener = connectionListener,
networkState = networkStatus,
recoveryCache = cacheController.rCache,
valveCache = cacheController.vCache,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.jeremy.thunder.state

sealed interface ActivityState

object Initialize: ActivityState

object GetReady: ActivityState

object ShutDown: ActivityState

0 comments on commit 9af7c17

Please sign in to comment.