2222 */
2323class IterableBackgroundInitializer {
2424 private static final String TAG = "IterableBackgroundInit" ;
25-
25+
2626 // Timeout for initialization to prevent indefinite hangs (5 seconds)
2727 private static final int INITIALIZATION_TIMEOUT_SECONDS = 5 ;
28-
28+
2929 // Callback manager for initialization completion
3030 private static final IterableInitializationCallbackManager callbackManager = new IterableInitializationCallbackManager ();
3131
@@ -168,43 +168,43 @@ static void initializeInBackground(@NonNull Context context,
168168 Runnable initTask = () -> {
169169 long startTime = System .currentTimeMillis ();
170170 boolean initSucceeded = false ;
171-
171+
172172 try {
173173 IterableLogger .d (TAG , "Starting initialization with " + INITIALIZATION_TIMEOUT_SECONDS + " second timeout" );
174-
174+
175175 // Submit the actual initialization task
176176 Future <?> initFuture = initExecutor .submit (() -> {
177177 IterableLogger .d (TAG , "Executing initialization on background thread" );
178178 IterableApi .initialize (context , apiKey , config );
179179 });
180-
180+
181181 // Wait for initialization with timeout
182182 initFuture .get (INITIALIZATION_TIMEOUT_SECONDS , TimeUnit .SECONDS );
183183 initSucceeded = true ;
184-
184+
185185 long elapsedTime = System .currentTimeMillis () - startTime ;
186186 IterableLogger .d (TAG , "Background initialization completed successfully in " + elapsedTime + "ms" );
187-
187+
188188 } catch (TimeoutException e ) {
189189 long elapsedTime = System .currentTimeMillis () - startTime ;
190190 IterableLogger .w (TAG , "Background initialization timed out after " + elapsedTime + "ms, continuing anyway" );
191191 // Cancel the hanging initialization task
192192 initExecutor .shutdownNow ();
193-
193+
194194 } catch (Exception e ) {
195195 long elapsedTime = System .currentTimeMillis () - startTime ;
196196 IterableLogger .e (TAG , "Background initialization encountered error after " + elapsedTime + "ms, but continuing" , e );
197197 }
198-
198+
199199 // Always mark as completed and call callbacks regardless of success/timeout/failure
200200 synchronized (initLock ) {
201201 isBackgroundInitialized = true ;
202202 isInitializing = false ;
203203 }
204-
204+
205205 // Process any queued operations
206206 operationQueue .processAll (backgroundExecutor );
207-
207+
208208 // Notify completion on main thread (always success)
209209 final boolean finalInitSucceeded = initSucceeded ;
210210 new Handler (Looper .getMainLooper ()).post (() -> {
@@ -215,7 +215,7 @@ static void initializeInBackground(@NonNull Context context,
215215 } else {
216216 IterableLogger .w (TAG , "Initialization timed out or failed, but notifying callbacks anyway after " + totalTime + "ms" );
217217 }
218-
218+
219219 // Call the original callback directly
220220 if (callback != null ) {
221221 try {
@@ -224,7 +224,7 @@ static void initializeInBackground(@NonNull Context context,
224224 IterableLogger .e (TAG , "Exception in initialization callback" , e );
225225 }
226226 }
227-
227+
228228 // Call all pending callbacks from concurrent initialization attempts
229229 IterableInitializationCallback pendingCallback ;
230230 while ((pendingCallback = pendingCallbacks .poll ()) != null ) {
@@ -234,12 +234,12 @@ static void initializeInBackground(@NonNull Context context,
234234 IterableLogger .e (TAG , "Exception in pending initialization callback" , e );
235235 }
236236 }
237-
237+
238238 } catch (Exception e ) {
239239 IterableLogger .e (TAG , "Exception in initialization completion notification" , e );
240240 }
241241 });
242-
242+
243243 // Clean up the init executor
244244 try {
245245 if (!initExecutor .isShutdown ()) {
@@ -368,20 +368,20 @@ private static void shutdownBackgroundExecutorAsync() {
368368 /**
369369 * Register a callback to be notified when SDK initialization completes.
370370 * If the SDK is already initialized, the callback is invoked immediately.
371- *
371+ *
372372 * @param callback The callback to be notified when initialization completes
373373 */
374374 static void onSDKInitialized (@ NonNull IterableInitializationCallback callback ) {
375375 callbackManager .addSubscriber (callback );
376376 }
377-
377+
378378 /**
379379 * Notify that initialization has completed - called by IterableApi.initialize()
380380 */
381381 static void notifyInitializationComplete () {
382382 callbackManager .notifyInitializationComplete ();
383383 }
384-
384+
385385 /**
386386 * Reset background initialization state - for testing only
387387 */
@@ -408,48 +408,49 @@ static void resetBackgroundInitializationState() {
408408 */
409409class IterableInitializationCallbackManager {
410410 private static final String TAG = "IterableInitCallbackMgr" ;
411-
411+
412412 // Thread-safe collections for callback management
413413 private final CopyOnWriteArraySet <IterableInitializationCallback > subscribers = new CopyOnWriteArraySet <>();
414414 private final ConcurrentLinkedQueue <IterableInitializationCallback > oneTimeCallbacks = new ConcurrentLinkedQueue <>();
415-
415+
416416 private volatile boolean isInitialized = false ;
417417 private final Object initLock = new Object ();
418-
418+
419419 /**
420420 * Add a callback that will be called every time initialization completes.
421421 * If initialization has already completed, the callback is called immediately.
422- *
422+ *
423423 * @param callback The callback to add (must not be null)
424424 */
425425 void addSubscriber (@ NonNull IterableInitializationCallback callback ) {
426426 if (callback == null ) {
427427 IterableLogger .w (TAG , "Cannot add null callback subscriber" );
428428 return ;
429429 }
430-
430+
431431 subscribers .add (callback );
432-
432+
433433 // If already initialized, call immediately on main thread
434434 synchronized (initLock ) {
435435 if (isInitialized ) {
436436 callCallbackOnMainThread (callback , "subscriber (immediate)" );
437+ subscribers .remove (callback ); // Auto-remove after calling
437438 }
438439 }
439440 }
440-
441+
441442 /**
442443 * Add a one-time callback that will be called once when initialization completes.
443444 * If initialization has already completed, the callback is called immediately.
444445 * This is used for the callback parameter in initialize() methods.
445- *
446+ *
446447 * @param callback The one-time callback to add (can be null)
447448 */
448449 void addOneTimeCallback (@ Nullable IterableInitializationCallback callback ) {
449450 if (callback == null ) {
450451 return ;
451452 }
452-
453+
453454 synchronized (initLock ) {
454455 if (isInitialized ) {
455456 // Call immediately if already initialized
@@ -460,7 +461,7 @@ void addOneTimeCallback(@Nullable IterableInitializationCallback callback) {
460461 }
461462 }
462463 }
463-
464+
464465 /**
465466 * Notify all callbacks that initialization has completed.
466467 * This should be called once when initialization finishes.
@@ -473,23 +474,24 @@ void notifyInitializationComplete() {
473474 }
474475 isInitialized = true ;
475476 }
476-
477- IterableLogger .d (TAG , "Notifying initialization completion to " +
478- subscribers .size () + " subscribers and " +
477+
478+ IterableLogger .d (TAG , "Notifying initialization completion to " +
479+ subscribers .size () + " subscribers and " +
479480 oneTimeCallbacks .size () + " one-time callbacks" );
480-
481- // Notify all subscribers
481+
482+ // Notify all subscribers and clear the list (auto-remove after calling)
482483 for (IterableInitializationCallback callback : subscribers ) {
483484 callCallbackOnMainThread (callback , "subscriber" );
484485 }
485-
486+ subscribers .clear (); // Auto-remove all subscribers after calling them
487+
486488 // Notify and clear one-time callbacks
487489 IterableInitializationCallback oneTimeCallback ;
488490 while ((oneTimeCallback = oneTimeCallbacks .poll ()) != null ) {
489491 callCallbackOnMainThread (oneTimeCallback , "one-time" );
490492 }
491493 }
492-
494+
493495 /**
494496 * Reset the initialization state - for testing only
495497 */
@@ -500,7 +502,7 @@ void reset() {
500502 oneTimeCallbacks .clear ();
501503 }
502504 }
503-
505+
504506 /**
505507 * Helper method to ensure callbacks are called on the main thread
506508 */
0 commit comments