Skip to main content
The TriggersProvider enables real-time reactive programming patterns by allowing you to observe cohort membership changes and trigger actions when users enter or exit specific cohorts. This is powerful for personalizing user experiences, A/B testing, and dynamic content delivery.

Overview

The Triggers Provider allows you to:
  • React to cohort changes in real-time - Execute code when users enter or exit cohorts
  • Observe activations - Monitor when users qualify for specific ad platform activations
  • Build personalized experiences - Show/hide content based on cohort membership
  • Implement A/B tests - Dynamically segment users and track their behavior

Creating a TriggersProvider

Create a TriggersProvider instance from your Permutive SDK instance:
import com.permutive.android.TriggersProvider

class MyActivity : AppCompatActivity() {

    private val permutive by lazy {
        (application as MyApplication).permutive
    }

    private val triggersProvider: TriggersProvider by lazy {
        permutive.triggersProvider()
    }

    // Use the triggers provider...
}
💡 Lifecycle Management You can create any number of TriggersProvider instances. They’re lightweight and don’t maintain heavy state.

Use Cases

1. Observing Cohort Membership

Monitor when a user is in a specific cohort:
import com.permutive.android.TriggerAction

class PersonalizedActivity : AppCompatActivity() {

    private var premiumContentTrigger: TriggerAction? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_personalized)

        val triggersProvider = permutive.triggersProvider()

        // Monitor if user is in the "premium_readers" cohort
        premiumContentTrigger = triggersProvider.triggerAction("premium_readers") { isInCohort ->
            if (isInCohort) {
                showPremiumContent()
            } else {
                showStandardContent()
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // Clean up the trigger when the activity is destroyed
        premiumContentTrigger?.close()
    }

    private fun showPremiumContent() {
        // Update UI to show premium features
        binding.premiumBanner.visibility = View.VISIBLE
        binding.subscriptionPrompt.visibility = View.GONE
    }

    private fun showStandardContent() {
        // Update UI for standard users
        binding.premiumBanner.visibility = View.GONE
        binding.subscriptionPrompt.visibility = View.VISIBLE
    }
}

2. Observing Multiple Cohorts

React to multiple cohort memberships:

Kotlin

class ContentRecommendationActivity : AppCompatActivity() {
    
    private val triggers = mutableListOf<TriggerAction>()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val triggersProvider = permutive.triggersProvider()
        
        // Sports enthusiasts
        triggers.add(
            triggersProvider.triggerAction("sports_enthusiast") { isInCohort ->
                if (isInCohort) addRecommendation("Sports")
            }
        )
        
        // Tech readers
        triggers.add(
            triggersProvider.triggerAction("tech_reader") { isInCohort ->
                if (isInCohort) addRecommendation("Technology")
            }
        )
        
        // Business professionals
        triggers.add(
            triggersProvider.triggerAction("business_professional") { isInCohort ->
                if (isInCohort) addRecommendation("Business")
            }
        )
    }
    
    override fun onDestroy() {
        super.onDestroy()
        triggers.forEach { it.close() }
        triggers.clear()
    }
    
    private fun addRecommendation(category: String) {
        // Update UI with personalized recommendations
    }
}

3. Observing Activations for Ad Platforms

Monitor cohorts activated for specific ad platforms:

Kotlin

class AdTargetingActivity : AppCompatActivity() {
    
    private var gamActivationTrigger: TriggerAction? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val triggersProvider = permutive.triggersProvider()
        
        // Monitor Google Ad Manager activations
        gamActivationTrigger = triggersProvider.cohortActivations("dfp") { cohorts ->
            Log.d("Permutive", "User has ${cohorts.size} active GAM cohorts: $cohorts")
            updateAdTargeting(cohorts)
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        gamActivationTrigger?.close()
    }
    
    private fun updateAdTargeting(cohorts: List<String>) {
        // Use cohorts for ad targeting
    }
}

Available Activation Types

Activation TypeDescription
"dfp"Google Ad Manager (formerly DoubleClick for Publishers) activations
"appnexus_adserver"Xandr/AppNexus ad server activations
"freewheel"Freewheel activations
"dfp_contextual"Google Ad Manager contextual cohorts
"appnexus_adserver_contextual"Xandr/AppNexus contextual cohorts

4. A/B Testing Based on Cohorts

Implement feature flags or A/B tests using cohort membership:

Kotlin

class ExperimentActivity : AppCompatActivity() {
    
    private var experimentTrigger: TriggerAction? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val triggersProvider = permutive.triggersProvider()
        
        // A/B test: Show new checkout flow to test cohort
        experimentTrigger = triggersProvider.triggerAction("checkout_experiment_v2") { isInTestGroup ->
            if (isInTestGroup) {
                showNewCheckoutFlow()
            } else {
                showStandardCheckoutFlow()
            }
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        experimentTrigger?.close()
    }
}

TriggerAction Lifecycle

The TriggerAction interface represents an active trigger subscription. It’s crucial to manage its lifecycle properly to prevent memory leaks.

Key Points

  1. Create triggers when you need to start observing
  2. Close triggers when you’re done (typically in onDestroy())
  3. Store references if you need to close them later
  4. Don’t recreate unnecessarily - triggers survive configuration changes if managed properly

Best Practices

✅ Good: Proper Lifecycle Management

class MyActivity : AppCompatActivity() {
    private var trigger: TriggerAction? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        trigger = triggersProvider.triggerAction("cohort_id") { /* ... */ }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        trigger?.close()
    }
}

✅ Good: Using ViewModel for Configuration Changes

class MyViewModel : ViewModel() {
    private var trigger: TriggerAction? = null
    
    fun observeCohort(triggersProvider: TriggersProvider) {
        if (trigger == null) {
            trigger = triggersProvider.triggerAction("cohort_id") { /* ... */ }
        }
    }
    
    override fun onCleared() {
        super.onCleared()
        trigger?.close()
    }
}

❌ Bad: Not Closing Triggers

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Creates a new trigger every time, never closes the old ones
        triggersProvider.triggerAction("cohort_id") { /* ... */ }
    }
    // Memory leak! Triggers never closed
}

Thread Safety

The TriggersProvider and TriggerAction are thread-safe. Callbacks are delivered on a background thread, so if you need to update UI, use appropriate threading mechanisms:
private var trigger: TriggerAction? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    trigger = triggersProvider.triggerAction("cohort_id") { isInCohort ->
        // Callback is on background thread
        lifecycleScope.launch(Dispatchers.Main) {
            // Update UI on main thread
            if (isInCohort) {
                textView.text = "User is in cohort"
            }
        }
    }
}

Advanced Patterns

Combining Multiple Cohorts

Trigger actions only when user is in multiple cohorts:
class AdvancedTargetingActivity : AppCompatActivity() {
    
    private var isPremium = false
    private var isSportsReader = false
    
    private val triggers = mutableListOf<TriggerAction>()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val triggersProvider = permutive.triggersProvider()
        
        triggers.add(
            triggersProvider.triggerAction("premium_subscriber") { inCohort ->
                isPremium = inCohort
                updateContent()
            }
        )
        
        triggers.add(
            triggersProvider.triggerAction("sports_enthusiast") { inCohort ->
                isSportsReader = inCohort
                updateContent()
            }
        )
    }
    
    private fun updateContent() {
        when {
            isPremium && isSportsReader -> showPremiumSportsContent()
            isPremium -> showPremiumGeneralContent()
            isSportsReader -> showFreeSportsContent()
            else -> showGeneralContent()
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        triggers.forEach { it.close() }
        triggers.clear()
    }
}

Deprecated APIs

Legacy Segment-Based Triggers

The following methods are deprecated as they only support integer-based segment IDs:
@Deprecated("Use triggerAction(cohortId: String, callback: Method<Boolean>)")
fun <T : Any> triggerAction(queryId: Int, callback: Method<T>): TriggerAction

@Deprecated("Use cohortActivations")
fun querySegments(callback: Method<List<Int>>): TriggerAction

@Deprecated("Use cohortActivations") 
fun queryReactions(reaction: String, callback: Method<List<Int>>): TriggerAction
Migration Guide: Old (Deprecated):
triggersProvider.triggerAction(12345) { result: Boolean ->
    // Handle result
}
New:
triggersProvider.triggerAction("cohort_id_string") { isInCohort ->
    // Handle result
}
📘 Why the Change? The SDK now supports Classification Model cohorts which use string IDs. The new APIs support both traditional integer-based cohorts and newer string-based cohorts seamlessly.

Troubleshooting

Problem: Your trigger callback is never invoked.Possible Causes:
  1. SDK hasn’t finished initializing
  2. Cohort ID is incorrect
  3. User hasn’t generated enough events to be segmented
Solutions:
  • Enable debug logging: permutive.setDeveloperMode(true)
  • Check logs for cohort updates
  • Verify cohort ID matches your dashboard configuration
  • Ensure events are being tracked (see Verification Guide)
Problem: App memory usage grows over time.Cause: TriggerAction instances not being closed.Solution: Always call close() on triggers in appropriate lifecycle methods (usually onDestroy() or onCleared()).
Problem: Crash when trying to update UI from callback.Cause: Callbacks execute on background threads.Solution: Use Handler, coroutines, or runOnUiThread() to update UI (see Thread Safety section above).

Best Practices

  • Close all triggers when done to prevent memory leaks
  • Handle threading appropriately when updating UI
  • Use descriptive cohort IDs that match your dashboard
  • Store trigger references if you need to close them later
  • Test with debug logging enabled

Example: Complete Implementation

Here’s a complete example showing proper implementation:
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.permutive.android.TriggerAction
import com.permutive.android.TriggersProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class PersonalizedHomeActivity : AppCompatActivity() {
    
    private val permutive by lazy {
        (application as MyApplication).permutive
    }
    
    private val triggersProvider by lazy {
        permutive.triggersProvider()
    }
    
    private val activeTriggers = mutableListOf<TriggerAction>()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_personalized_home)
        
        setupCohortTriggers()
    }
    
    private fun setupCohortTriggers() {
        // Premium content trigger
        activeTriggers.add(
            triggersProvider.triggerAction("premium_subscriber") { isSubscriber ->
                lifecycleScope.launch(Dispatchers.Main) {
                    updatePremiumFeatures(isSubscriber)
                }
            }
        )
        
        // Sports content trigger
        activeTriggers.add(
            triggersProvider.triggerAction("sports_enthusiast") { isSportsReader ->
                lifecycleScope.launch(Dispatchers.Main) {
                    updateSportsRecommendations(isSportsReader)
                }
            }
        )
        
        // Ad activations trigger
        activeTriggers.add(
            triggersProvider.cohortActivations("dfp") { cohorts ->
                lifecycleScope.launch(Dispatchers.Main) {
                    updateAdTargeting(cohorts)
                }
            }
        )
    }
    
    private fun updatePremiumFeatures(isPremium: Boolean) {
        binding.premiumBadge.visibility = if (isPremium) View.VISIBLE else View.GONE
        binding.upgradePrompt.visibility = if (isPremium) View.GONE else View.VISIBLE
    }
    
    private fun updateSportsRecommendations(showSports: Boolean) {
        if (showSports) {
            binding.sportsSection.visibility = View.VISIBLE
            loadSportsContent()
        } else {
            binding.sportsSection.visibility = View.GONE
        }
    }
    
    private fun updateAdTargeting(cohorts: List<String>) {
        Log.d("Permutive", "Updated ad targeting with ${cohorts.size} cohorts")
        // Ad targeting logic here
    }
    
    private fun loadSportsContent() {
        // Load sports-specific content
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // Clean up all triggers
        activeTriggers.forEach { it.close() }
        activeTriggers.clear()
    }
}


API Reference

For complete API documentation, see the Javadocs.

Key Interfaces

  • TriggersProvider - Factory for creating trigger subscriptions
  • TriggerAction - Represents an active trigger subscription
  • Method<T> - Callback interface for Java compatibility