@@ -10,20 +10,25 @@ import com.devcycle.sdk.android.model.*
1010import com.devcycle.sdk.android.util.DevCycleLogger
1111import com.devcycle.sdk.android.util.DVCSharedPrefs
1212import com.devcycle.sdk.android.util.LogLevel
13+ import com.launchdarkly.eventsource.ConnectStrategy
14+ import com.launchdarkly.eventsource.EventSource
15+ import com.launchdarkly.eventsource.background.BackgroundEventSource
16+ import com.launchdarkly.eventsource.MessageEvent
17+ import com.launchdarkly.eventsource.ReadyState
1318import kotlinx.coroutines.*
1419import kotlinx.coroutines.sync.Mutex
1520import kotlinx.coroutines.sync.withLock
16- import org.jetbrains.annotations.TestOnly
1721import org.json.JSONArray
1822import org.json.JSONObject
1923import java.lang.ref.WeakReference
2024import java.net.URI
2125import java.util.concurrent.ConcurrentLinkedQueue
22- import java.util.concurrent.ExecutorService
23- import java.util.concurrent.Executors
26+ import java.util.concurrent.TimeUnit
2427import java.util.concurrent.atomic.AtomicBoolean
2528import kotlin.coroutines.CoroutineContext
2629
30+ private const val EVENT_SOURCE_RETRY_DELAY_MIN : Long = 5
31+
2732/* *
2833 * Main entry point for SDK user
2934 * The class is constructed by calling DevCycleClient.builder(){builder options}.build()
@@ -43,8 +48,7 @@ class DevCycleClient private constructor(
4348 private val coroutineContext : CoroutineContext = Dispatchers .Default
4449) {
4550 private var config: BucketedUserConfig ? = null
46- private var eventSource: EventSource ? = null
47- private var executorService: ExecutorService ? = null
51+ private var backgroundEventSource: BackgroundEventSource ? = null
4852 private val defaultIntervalInMs: Long = 10000
4953 private val flushInMs: Long = options?.flushEventsIntervalMs ? : defaultIntervalInMs
5054 private val dvcSharedPrefs: DVCSharedPrefs = DVCSharedPrefs (context)
@@ -90,8 +94,11 @@ class DevCycleClient private constructor(
9094 initEventSource()
9195 val application : Application = context.applicationContext as Application
9296
93- val lifecycleCallbacks = DVCLifecycleCallbacks (onPauseApplication, onResumeApplication,
94- config?.sse?.inactivityDelay?.toLong(), customLifecycleHandler
97+ val lifecycleCallbacks = DVCLifecycleCallbacks (
98+ onPauseApplication,
99+ onResumeApplication,
100+ config?.sse?.inactivityDelay?.toLong(),
101+ customLifecycleHandler
95102 )
96103 application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
97104 }
@@ -114,16 +121,16 @@ class DevCycleClient private constructor(
114121 coroutineScope.launch {
115122 withContext(Dispatchers .IO ) {
116123 DevCycleLogger .d(" Closing Realtime Updates connection" )
117- eventSource ?.close()
124+ backgroundEventSource ?.close()
118125 }
119126 }
120127 }
121128
122129 private val onResumeApplication = fun () {
123- if (eventSource?.state != ReadyState .OPEN ) {
130+ if (backgroundEventSource?. eventSource?.state != ReadyState .OPEN ) {
124131 coroutineScope.launch {
125132 withContext(Dispatchers .IO ) {
126- eventSource ?.close()
133+ backgroundEventSource ?.close()
127134 DevCycleLogger .d(" Attempting to restart Realtime Updates connection" )
128135 initEventSource()
129136 refetchConfig(false , null , null )
@@ -138,11 +145,16 @@ class DevCycleClient private constructor(
138145 return
139146 }
140147 if (config?.sse?.url == null ) { return }
141- eventSource = EventSource .Builder (Handler (fun (messageEvent : MessageEvent ? ) {
142- if (messageEvent == null ) { return }
148+
149+ val handler = SSEEventHandler (fun (messageEvent : MessageEvent ? ) {
150+ if (messageEvent == null ) {
151+ return
152+ }
143153
144154 val data = JSONObject (messageEvent.data)
145- if (! data.has(" data" )) { return }
155+ if (! data.has(" data" )) {
156+ return
157+ }
146158
147159 val innerData = JSONObject (data.get(" data" ) as String )
148160 val lastModified = if (innerData.has(" lastModified" )) {
@@ -156,17 +168,17 @@ class DevCycleClient private constructor(
156168 } else null
157169
158170 if (type == " refetchConfig" || type == " " ) { // Refetch the config if theres no type
171+ DevCycleLogger .d(" SSE Message: Refetching config" )
159172 refetchConfig(true , lastModified, etag)
160173 }
161- }), URI (config?.sse?.url)).executor(this .getValidExecutorService()).build()
162- eventSource?.start()
163- }
174+ })
175+ val builder = EventSource .Builder (
176+ ConnectStrategy .http(URI (config?.sse?.url))
177+ .readTimeout(EVENT_SOURCE_RETRY_DELAY_MIN , TimeUnit .MINUTES )
178+ )
164179
165- private fun getValidExecutorService (): ExecutorService ? {
166- if (executorService == null || executorService?.isTerminated == true ) {
167- executorService = Executors .newSingleThreadExecutor()
168- }
169- return executorService
180+ backgroundEventSource = BackgroundEventSource .Builder (handler, builder).build()
181+ backgroundEventSource?.start()
170182 }
171183
172184 fun onInitialized (callback : DevCycleCallback <String >) {
@@ -267,7 +279,7 @@ class DevCycleClient private constructor(
267279 fun close (callback : DevCycleCallback <String >? = null) {
268280 coroutineScope.launch {
269281 withContext(Dispatchers .IO ) {
270- eventSource ?.close()
282+ backgroundEventSource ?.close()
271283 }
272284 withContext(coroutineContext) {
273285 eventQueue.close(callback)
0 commit comments