@@ -179,6 +179,65 @@ open class Nimbus(
179179 }
180180 }
181181
182+ public val updates = object {
183+ private val lock = ReentrantLock ()
184+ private val callbackMap: MutableMap <String , MutableSet <() - > Unit >> = mutableMapOf ()
185+
186+ @AnyThread
187+ public fun register (featureId : String , callback : () -> Unit ) {
188+ lock.runBlock {
189+ callbackMap
190+ .getOrPut(featureId, arrayListOf)
191+ .add(callback)
192+ }
193+ }
194+
195+ @AnyThread
196+ public fun unregister (featureId : String , callback : () -> Unit ) {
197+ lock.runBlock {
198+ callbackMap.get(featureId)?.run { remove(callback) }
199+ }
200+ }
201+
202+ @AnyThread
203+ public fun notifyChanged (events : List <EnrollmentChangeEvent >) {
204+ if (events.isEmpty()) {
205+ return
206+ }
207+
208+ val featureIds: MutableSet <String > = mutableSetOf ()
209+
210+ for (event in events) {
211+ for (featureId in event.featureIds) {
212+ featureIds.add(featureId)
213+ }
214+ }
215+
216+ notifyFeatures(featureIds)
217+ }
218+
219+ @AnyThread
220+ public fun notifyFeatures (featureIds : Set <String >) {
221+ val toUpdate = arrayListOf ()
222+
223+ lock.runBlock {
224+ for (featureId in featureIds) {
225+ callbackMap.get(featureId)?.also { callbacks ->
226+ for (callback of callbacks) {
227+ toUpdate.add(callback)
228+ }
229+ }
230+ }
231+ }
232+
233+ scope.launch(Dispatchers .Main ) {
234+ for (callback of toUpdate) {
235+ callback()
236+ }
237+ }
238+ }
239+ }
240+
182241 init {
183242 NullVariables .instance.setContext(context)
184243
@@ -279,7 +338,7 @@ open class Nimbus(
279338 @VisibleForTesting(otherwise = VisibleForTesting .PRIVATE )
280339 internal fun initializeOnThisThread () = withCatchAll(" initialize" ) {
281340 nimbusClient.initialize()
282- postEnrolmentCalculation()
341+ postEnrolmentCalculation(true )
283342 }
284343
285344 override fun fetchExperiments () {
@@ -344,7 +403,8 @@ open class Nimbus(
344403 NimbusHealth .applyPendingExperimentsTime.accumulateSingleSample(time)
345404 recordExperimentTelemetryEvents(events!! )
346405 // Get the experiments to record in telemetry
347- postEnrolmentCalculation()
406+ postEnrolmentCalculation(false )
407+ updates.notifyChanged(events!! )
348408 } catch (e: NimbusException .InvalidExperimentFormat ) {
349409 reportError(" Invalid experiment format" , e)
350410 }
@@ -379,11 +439,22 @@ open class Nimbus(
379439 }
380440
381441 @WorkerThread
382- private fun postEnrolmentCalculation () {
383- nimbusClient.getActiveExperiments().let {
384- recordExperimentTelemetry(it )
442+ private fun postEnrolmentCalculation (initial : Bool ) {
443+ nimbusClient.getActiveExperiments().also { experiments ->
444+ recordExperimentTelemetry(experiments )
385445 updateObserver { observer ->
386- observer.onUpdatesApplied(it)
446+ observer.onUpdatesApplied(experiments)
447+ }
448+
449+ if (initial) {
450+ val featureIds = mutableSetOf ()
451+ for (experiment in experiments) {
452+ for (featureId in experiment.featureIds) {
453+ featureIds.add(featureId)
454+ }
455+ }
456+
457+ updates.notifyFeatures(featureIds)
387458 }
388459 }
389460 }
@@ -431,7 +502,8 @@ open class Nimbus(
431502 val enrolmentChanges = nimbusClient.setExperimentParticipation(active)
432503 if (enrolmentChanges.isNotEmpty()) {
433504 recordExperimentTelemetryEvents(enrolmentChanges)
434- postEnrolmentCalculation()
505+ postEnrolmentCalculation(false )
506+ updates.notifyChanged(enrolmentChanges)
435507 }
436508 }
437509
@@ -442,7 +514,8 @@ open class Nimbus(
442514 val enrolmentChanges = nimbusClient.setRolloutParticipation(active)
443515 if (enrolmentChanges.isNotEmpty()) {
444516 recordExperimentTelemetryEvents(enrolmentChanges)
445- postEnrolmentCalculation()
517+ postEnrolmentCalculation(false )
518+ updates.notifyChanged(enrolmentChanges)
446519 }
447520 }
448521
0 commit comments