Skip to content

Commit b72eb2d

Browse files
authored
Merge branch 'FME-11237_baseline' into FME-11238-split-events_3
2 parents de7f159 + 905d5e4 commit b72eb2d

File tree

6 files changed

+468
-0
lines changed

6 files changed

+468
-0
lines changed

events-domain/src/main/java/io/split/android/client/events/.gitkeep

Whitespace-only changes.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package io.split.android.client.events;
2+
3+
import androidx.annotation.NonNull;
4+
5+
import java.util.Collections;
6+
import java.util.Set;
7+
import java.util.concurrent.ArrayBlockingQueue;
8+
import java.util.concurrent.ConcurrentHashMap;
9+
import java.util.concurrent.ThreadFactory;
10+
import java.util.concurrent.atomic.AtomicInteger;
11+
12+
import io.split.android.client.utils.logger.Logger;
13+
import io.split.android.engine.scheduler.PausableThreadPoolExecutor;
14+
import io.split.android.engine.scheduler.PausableThreadPoolExecutorImpl;
15+
16+
public abstract class BaseEventsManager implements Runnable {
17+
18+
private final static int QUEUE_CAPACITY = 20;
19+
// Shared thread factory for all instances
20+
private static final ThreadFactory EVENTS_THREAD_FACTORY = createThreadFactory();
21+
22+
protected final ArrayBlockingQueue<SplitInternalEvent> mQueue;
23+
24+
protected final Set<SplitInternalEvent> mTriggered;
25+
26+
private static ThreadFactory createThreadFactory() {
27+
final AtomicInteger threadNumber = new AtomicInteger(1);
28+
return new ThreadFactory() {
29+
@Override
30+
public Thread newThread(Runnable r) {
31+
Thread thread = new Thread(r, "Split-FactoryEventsManager-" + threadNumber.getAndIncrement());
32+
thread.setDaemon(true);
33+
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
34+
@Override
35+
public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
36+
Logger.e("Unexpected error " + e.getLocalizedMessage());
37+
}
38+
});
39+
return thread;
40+
}
41+
};
42+
}
43+
44+
public BaseEventsManager() {
45+
mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
46+
mTriggered = Collections.newSetFromMap(new ConcurrentHashMap<>());
47+
launch(EVENTS_THREAD_FACTORY);
48+
}
49+
50+
@Override
51+
public void run() {
52+
// This code was intentionally designed this way
53+
// noinspection InfiniteLoopStatement
54+
while (true) {
55+
triggerEventsWhenAreAvailable();
56+
}
57+
}
58+
59+
private void launch(ThreadFactory threadFactory) {
60+
PausableThreadPoolExecutor mScheduler = PausableThreadPoolExecutorImpl.newSingleThreadExecutor(threadFactory);
61+
mScheduler.submit(this);
62+
mScheduler.resume();
63+
}
64+
65+
protected abstract void triggerEventsWhenAreAvailable();
66+
67+
protected abstract void notifyInternalEvent(SplitInternalEvent event);
68+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package io.split.android.client.events.delivery;
2+
3+
import androidx.annotation.NonNull;
4+
5+
import java.util.concurrent.Executor;
6+
7+
import io.harness.events.EventHandler;
8+
import io.harness.events.EventsManager;
9+
import io.harness.events.Logging;
10+
import io.split.android.client.events.logging.SplitLogging;
11+
12+
/**
13+
* Utility for registering event handlers that need to execute on two different threads.
14+
* <p>
15+
* This is useful when an event should trigger both background work and UI updates.
16+
* Each callback is wrapped with its executor before registration.
17+
*
18+
* @param <E> event type
19+
* @param <I> internal event type (for EventsManager)
20+
* @param <M> metadata type
21+
*/
22+
public class DualExecutorRegistration<E, I, M> {
23+
24+
@NonNull
25+
private final Executor mBackgroundExecutor;
26+
@NonNull
27+
private final Executor mMainThreadExecutor;
28+
@NonNull
29+
private final Logging mLogging;
30+
31+
/**
32+
* Creates a new DualExecutorRegistration with a {@link SplitLogging} instance.
33+
*
34+
* @param backgroundExecutor executor for background execution
35+
* @param mainThreadExecutor executor for main thread execution
36+
*/
37+
public DualExecutorRegistration(@NonNull Executor backgroundExecutor,
38+
@NonNull Executor mainThreadExecutor) {
39+
this(backgroundExecutor, mainThreadExecutor, new SplitLogging());
40+
}
41+
42+
/**
43+
* Creates a new DualExecutorRegistration.
44+
* <p>
45+
* Package-private for testing.
46+
*
47+
* @param backgroundExecutor executor for background execution
48+
* @param mainThreadExecutor executor for main thread execution
49+
* @param logging logging instance
50+
*/
51+
DualExecutorRegistration(@NonNull Executor backgroundExecutor,
52+
@NonNull Executor mainThreadExecutor,
53+
@NonNull Logging logging) {
54+
if (backgroundExecutor == null) {
55+
throw new IllegalArgumentException("backgroundExecutor cannot be null");
56+
}
57+
if (mainThreadExecutor == null) {
58+
throw new IllegalArgumentException("mainThreadExecutor cannot be null");
59+
}
60+
if (logging == null) {
61+
throw new IllegalArgumentException("logging cannot be null");
62+
}
63+
mBackgroundExecutor = backgroundExecutor;
64+
mMainThreadExecutor = mainThreadExecutor;
65+
mLogging = logging;
66+
}
67+
68+
/**
69+
* Registers two handlers for the same event, each executing on its respective thread.
70+
*
71+
* @param eventsManager the events manager to register with
72+
* @param event the event to register for
73+
* @param backgroundCallback callback to execute on the background thread
74+
* @param mainThreadCallback callback to execute on the main thread
75+
*/
76+
public void register(@NonNull EventsManager<E, I, M> eventsManager,
77+
@NonNull E event,
78+
@NonNull EventHandler<E, M> backgroundCallback,
79+
@NonNull EventHandler<E, M> mainThreadCallback) {
80+
if (eventsManager == null || event == null) {
81+
return;
82+
}
83+
84+
if (backgroundCallback != null) {
85+
eventsManager.register(event, wrapWithExecutor(backgroundCallback, mBackgroundExecutor));
86+
}
87+
88+
if (mainThreadCallback != null) {
89+
eventsManager.register(event, wrapWithExecutor(mainThreadCallback, mMainThreadExecutor));
90+
}
91+
}
92+
93+
/**
94+
* Registers a single handler for the background thread only.
95+
*
96+
* @param eventsManager the events manager to register with
97+
* @param event the event to register for
98+
* @param backgroundCallback callback to execute on the background thread
99+
*/
100+
public void registerBackground(@NonNull EventsManager<E, I, M> eventsManager,
101+
@NonNull E event,
102+
@NonNull EventHandler<E, M> backgroundCallback) {
103+
if (eventsManager == null || event == null || backgroundCallback == null) {
104+
return;
105+
}
106+
eventsManager.register(event, wrapWithExecutor(backgroundCallback, mBackgroundExecutor));
107+
}
108+
109+
/**
110+
* Registers a single handler for the main thread only.
111+
*
112+
* @param eventsManager the events manager to register with
113+
* @param event the event to register for
114+
* @param mainThreadCallback callback to execute on the main thread
115+
*/
116+
public void registerMainThread(@NonNull EventsManager<E, I, M> eventsManager,
117+
@NonNull E event,
118+
@NonNull EventHandler<E, M> mainThreadCallback) {
119+
if (eventsManager == null || event == null || mainThreadCallback == null) {
120+
return;
121+
}
122+
eventsManager.register(event, wrapWithExecutor(mainThreadCallback, mMainThreadExecutor));
123+
}
124+
125+
private EventHandler<E, M> wrapWithExecutor(EventHandler<E, M> handler, Executor executor) {
126+
return (event, metadata) -> executor.execute(() -> {
127+
try {
128+
handler.handle(event, metadata);
129+
} catch (Exception e) {
130+
mLogging.logError("Exception in event handler: " + e.getMessage());
131+
}
132+
});
133+
}
134+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.split.android.client.events.logging;
2+
3+
import io.harness.events.Logging;
4+
import io.split.android.client.utils.logger.Logger;
5+
6+
/**
7+
* Implementation of {@link Logging} that delegates to the Split SDK {@link Logger}.
8+
*/
9+
public class SplitLogging implements Logging {
10+
11+
@Override
12+
public void logError(String message) {
13+
Logger.e(message);
14+
}
15+
16+
@Override
17+
public void logWarning(String message) {
18+
Logger.w(message);
19+
}
20+
21+
@Override
22+
public void logInfo(String message) {
23+
Logger.i(message);
24+
}
25+
26+
@Override
27+
public void logDebug(String message) {
28+
Logger.d(message);
29+
}
30+
31+
@Override
32+
public void logVerbose(String message) {
33+
Logger.v(message);
34+
}
35+
}

events-domain/src/test/java/io/split/android/client/events/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)