Skip to main content

Exemplos com Flow

Exemplos práticos de como usar o GoAB SDK com Kotlin Flow para reatividade.

Configuração com Flow

Setup Básico

class MainActivity : AppCompatActivity() {
private lateinit var goabSDK: GoABSDK
private val _experimentValues = MutableStateFlow<Map<String, Any>>(emptyMap())
val experimentValues: StateFlow<Map<String, Any>> = _experimentValues.asStateFlow()

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

setupSDK()
observeExperiments()
}

private fun setupSDK() {
goabSDK = GoABSDKFactory.create(
context = this,
accountId = 12345,
apiToken = "your-api-token",
timeoutSeconds = 30
)

lifecycleScope.launch {
goabSDK.initialize()
loadExperiments()
}
}

private fun loadExperiments() {
val experiments = mapOf(
"button_text" to goabSDK.getValue("button_text", "Clique Aqui"),
"show_banner" to goabSDK.getValue("show_banner", true),
"button_color" to goabSDK.getValue("button_color", "#FF0000")
)
_experimentValues.value = experiments
}

private fun observeExperiments() {
lifecycleScope.launch {
experimentValues.collect { experiments ->
applyExperiments(experiments)
}
}
}

private fun applyExperiments(experiments: Map<String, Any>) {
// Aplicar experimentos na UI
findViewById<Button>(R.id.button).text = experiments["button_text"].toString()
findViewById<Button>(R.id.button).setBackgroundColor(
Color.parseColor(experiments["button_color"].toString())
)
findViewById<View>(R.id.banner).visibility =
if (experiments["show_banner"] as Boolean) View.VISIBLE else View.GONE
}
}

ViewModel com Flow

ExperimentViewModel

class ExperimentViewModel(application: Application) : AndroidViewModel(application) {
private val goabSDK: GoABSDK by lazy {
GoABSDKFactory.create(
context = application,
accountId = 12345,
apiToken = "your-api-token",
timeoutSeconds = 30
)
}

private val _experiments = MutableStateFlow<Map<String, Any>>(emptyMap())
val experiments: StateFlow<Map<String, Any>> = _experiments.asStateFlow()

private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

private val _error = MutableStateFlow<String?>(null)
val error: StateFlow<String?> = _error.asStateFlow()

init {
initializeSDK()
}

private fun initializeSDK() {
viewModelScope.launch {
try {
_isLoading.value = true
goabSDK.initialize()
loadExperiments()
} catch (e: Exception) {
_error.value = "Erro ao inicializar SDK: ${e.message}"
} finally {
_isLoading.value = false
}
}
}

private fun loadExperiments() {
val experiments = mapOf(
"button_text" to goabSDK.getValue("button_text", "Clique Aqui"),
"show_banner" to goabSDK.getValue("show_banner", true),
"button_color" to goabSDK.getValue("button_color", "#FF0000"),
"max_retries" to goabSDK.getValue("max_retries", 3),
"api_endpoint" to goabSDK.getValue("api_endpoint", "https://api.prod.com")
)
_experiments.value = experiments
}

fun refreshExperiments() {
viewModelScope.launch {
try {
_isLoading.value = true
goabSDK.refreshExperiments()
loadExperiments()
} catch (e: Exception) {
_error.value = "Erro ao atualizar experimentos: ${e.message}"
} finally {
_isLoading.value = false
}
}
}

fun clearError() {
_error.value = null
}
}

Uso na Activity

class MainActivity : AppCompatActivity() {
private lateinit var viewModel: ExperimentViewModel

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

viewModel = ViewModelProvider(this)[ExperimentViewModel::class.java]
observeViewModel()
}

private fun observeViewModel() {
// Observar experimentos
lifecycleScope.launch {
viewModel.experiments.collect { experiments ->
applyExperiments(experiments)
}
}

// Observar estado de carregamento
lifecycleScope.launch {
viewModel.isLoading.collect { isLoading ->
findViewById<ProgressBar>(R.id.progress_bar).visibility =
if (isLoading) View.VISIBLE else View.GONE
}
}

// Observar erros
lifecycleScope.launch {
viewModel.error.collect { error ->
error?.let {
Toast.makeText(this, it, Toast.LENGTH_LONG).show()
viewModel.clearError()
}
}
}
}

private fun applyExperiments(experiments: Map<String, Any>) {
// Aplicar experimentos na UI
findViewById<Button>(R.id.button).text = experiments["button_text"].toString()
findViewById<Button>(R.id.button).setBackgroundColor(
Color.parseColor(experiments["button_color"].toString())
)
findViewById<View>(R.id.banner).visibility =
if (experiments["show_banner"] as Boolean) View.VISIBLE else View.GONE
}

private fun onRefreshClick() {
viewModel.refreshExperiments()
}
}

Flow com Transformações

ExperimentFlowManager

class ExperimentFlowManager(private val goabSDK: GoABSDK) {

// Flow de experimentos individuais
fun getButtonTextFlow(): Flow<String> = flow {
emit(goabSDK.getValue("button_text", "Clique Aqui") as String)
}

fun getShowBannerFlow(): Flow<Boolean> = flow {
emit(goabSDK.getValue("show_banner", true) as Boolean)
}

fun getButtonColorFlow(): Flow<String> = flow {
emit(goabSDK.getValue("button_color", "#FF0000") as String)
}

// Flow combinado de todos os experimentos
fun getAllExperimentsFlow(): Flow<Map<String, Any>> = combine(
getButtonTextFlow(),
getShowBannerFlow(),
getButtonColorFlow()
) { buttonText, showBanner, buttonColor ->
mapOf(
"button_text" to buttonText,
"show_banner" to showBanner,
"button_color" to buttonColor
)
}

// Flow com transformações
fun getButtonTextWithPrefix(): Flow<String> = getButtonTextFlow()
.map { text -> "🔘 $text" }
.distinctUntilChanged()

fun getButtonColorAsColor(): Flow<Int> = getButtonColorFlow()
.map { colorString -> Color.parseColor(colorString) }
.distinctUntilChanged()

// Flow com filtros
fun getBannerVisibilityFlow(): Flow<Int> = getShowBannerFlow()
.map { showBanner -> if (showBanner) View.VISIBLE else View.GONE }
.distinctUntilChanged()
}

Uso com Transformações

class MainActivity : AppCompatActivity() {
private lateinit var goabSDK: GoABSDK
private lateinit var experimentManager: ExperimentFlowManager

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

setupSDK()
observeExperiments()
}

private fun setupSDK() {
goabSDK = GoABSDKFactory.create(
context = this,
accountId = 12345,
apiToken = "your-api-token",
timeoutSeconds = 30
)

experimentManager = ExperimentFlowManager(goabSDK)

lifecycleScope.launch {
goabSDK.initialize()
}
}

private fun observeExperiments() {
// Observar texto do botão com prefixo
lifecycleScope.launch {
experimentManager.getButtonTextWithPrefix()
.collect { buttonText ->
findViewById<Button>(R.id.button).text = buttonText
}
}

// Observar cor do botão
lifecycleScope.launch {
experimentManager.getButtonColorAsColor()
.collect { buttonColor ->
findViewById<Button>(R.id.button).setBackgroundColor(buttonColor)
}
}

// Observar visibilidade do banner
lifecycleScope.launch {
experimentManager.getBannerVisibilityFlow()
.collect { visibility ->
findViewById<View>(R.id.banner).visibility = visibility
}
}

// Observar todos os experimentos
lifecycleScope.launch {
experimentManager.getAllExperimentsFlow()
.collect { experiments ->
Log.d("Experiments", "Experimentos atualizados: $experiments")
}
}
}
}

Flow com Cache e Refresh

CachedExperimentManager

class CachedExperimentManager(private val goabSDK: GoABSDK) {
private val _experiments = MutableStateFlow<Map<String, Any>>(emptyMap())
val experiments: StateFlow<Map<String, Any>> = _experiments.asStateFlow()

private val _isRefreshing = MutableStateFlow(false)
val isRefreshing: StateFlow<Boolean> = _isRefreshing.asStateFlow()

private val _lastRefresh = MutableStateFlow<Long?>(null)
val lastRefresh: StateFlow<Long?> = _lastRefresh.asStateFlow()

suspend fun loadExperiments() {
val experiments = mapOf(
"button_text" to goabSDK.getValue("button_text", "Clique Aqui"),
"show_banner" to goabSDK.getValue("show_banner", true),
"button_color" to goabSDK.getValue("button_color", "#FF0000"),
"max_retries" to goabSDK.getValue("max_retries", 3),
"api_endpoint" to goabSDK.getValue("api_endpoint", "https://api.prod.com")
)
_experiments.value = experiments
_lastRefresh.value = System.currentTimeMillis()
}

suspend fun refreshExperiments() {
_isRefreshing.value = true
try {
goabSDK.refreshExperiments()
loadExperiments()
} catch (e: Exception) {
Log.e("CachedExperimentManager", "Erro ao atualizar experimentos", e)
} finally {
_isRefreshing.value = false
}
}

fun getExperimentValue(key: String, defaultValue: Any): Any {
return _experiments.value[key] ?: defaultValue
}

// Flow que emite quando experimentos específicos mudam
fun getExperimentFlow(key: String, defaultValue: Any): Flow<Any> = experiments
.map { it[key] ?: defaultValue }
.distinctUntilChanged()

// Flow que emite quando qualquer experimento muda
fun getAnyExperimentChangeFlow(): Flow<Map<String, Any>> = experiments
.distinctUntilChanged()
}

Uso com Cache

class MainActivity : AppCompatActivity() {
private lateinit var goabSDK: GoABSDK
private lateinit var experimentManager: CachedExperimentManager

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

setupSDK()
observeExperiments()
}

private fun setupSDK() {
goabSDK = GoABSDKFactory.create(
context = this,
accountId = 12345,
apiToken = "your-api-token",
timeoutSeconds = 30
)

experimentManager = CachedExperimentManager(goabSDK)

lifecycleScope.launch {
goabSDK.initialize()
experimentManager.loadExperiments()
}
}

private fun observeExperiments() {
// Observar mudanças em experimentos específicos
lifecycleScope.launch {
experimentManager.getExperimentFlow("button_text", "Clique Aqui")
.collect { buttonText ->
findViewById<Button>(R.id.button).text = buttonText.toString()
}
}

lifecycleScope.launch {
experimentManager.getExperimentFlow("show_banner", true)
.collect { showBanner ->
findViewById<View>(R.id.banner).visibility =
if (showBanner as Boolean) View.VISIBLE else View.GONE
}
}

// Observar estado de refresh
lifecycleScope.launch {
experimentManager.isRefreshing.collect { isRefreshing ->
findViewById<ProgressBar>(R.id.refresh_progress).visibility =
if (isRefreshing) View.VISIBLE else View.GONE
}
}

// Observar última atualização
lifecycleScope.launch {
experimentManager.lastRefresh.collect { lastRefresh ->
lastRefresh?.let {
val timeAgo = System.currentTimeMillis() - it
findViewById<TextView>(R.id.last_update).text =
"Última atualização: ${timeAgo / 1000}s atrás"
}
}
}
}

private fun onRefreshClick() {
lifecycleScope.launch {
experimentManager.refreshExperiments()
}
}
}

Flow com Error Handling

SafeExperimentFlowManager

class SafeExperimentFlowManager(private val goabSDK: GoABSDK) {

fun getExperimentFlow(key: String, defaultValue: Any): Flow<Result<Any>> = flow {
try {
val value = goabSDK.getValue(key, defaultValue)
emit(Result.success(value))
} catch (e: Exception) {
emit(Result.failure(e))
}
}

fun getAllExperimentsFlow(): Flow<Result<Map<String, Any>>> = flow {
try {
val experiments = mapOf(
"button_text" to goabSDK.getValue("button_text", "Clique Aqui"),
"show_banner" to goabSDK.getValue("show_banner", true),
"button_color" to goabSDK.getValue("button_color", "#FF0000")
)
emit(Result.success(experiments))
} catch (e: Exception) {
emit(Result.failure(e))
}
}

fun getExperimentWithRetry(key: String, defaultValue: Any, maxRetries: Int = 3): Flow<Result<Any>> = flow {
var retries = 0
while (retries < maxRetries) {
try {
val value = goabSDK.getValue(key, defaultValue)
emit(Result.success(value))
return@flow
} catch (e: Exception) {
retries++
if (retries >= maxRetries) {
emit(Result.failure(e))
return@flow
}
delay(1000 * retries) // Backoff exponencial
}
}
}
}

Uso com Error Handling

class MainActivity : AppCompatActivity() {
private lateinit var goabSDK: GoABSDK
private lateinit var experimentManager: SafeExperimentFlowManager

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

setupSDK()
observeExperiments()
}

private fun setupSDK() {
goabSDK = GoABSDKFactory.create(
context = this,
accountId = 12345,
apiToken = "your-api-token",
timeoutSeconds = 30
)

experimentManager = SafeExperimentFlowManager(goabSDK)

lifecycleScope.launch {
goabSDK.initialize()
}
}

private fun observeExperiments() {
// Observar experimentos com tratamento de erro
lifecycleScope.launch {
experimentManager.getExperimentFlow("button_text", "Clique Aqui")
.collect { result ->
result.fold(
onSuccess = { buttonText ->
findViewById<Button>(R.id.button).text = buttonText.toString()
},
onFailure = { error ->
Log.e("MainActivity", "Erro ao obter button_text", error)
findViewById<Button>(R.id.button).text = "Clique Aqui"
}
)
}
}

// Observar experimentos com retry
lifecycleScope.launch {
experimentManager.getExperimentWithRetry("show_banner", true, maxRetries = 3)
.collect { result ->
result.fold(
onSuccess = { showBanner ->
findViewById<View>(R.id.banner).visibility =
if (showBanner as Boolean) View.VISIBLE else View.GONE
},
onFailure = { error ->
Log.e("MainActivity", "Erro ao obter show_banner após retry", error)
findViewById<View>(R.id.banner).visibility = View.GONE
}
)
}
}

// Observar todos os experimentos
lifecycleScope.launch {
experimentManager.getAllExperimentsFlow()
.collect { result ->
result.fold(
onSuccess = { experiments ->
applyExperiments(experiments)
},
onFailure = { error ->
Log.e("MainActivity", "Erro ao obter experimentos", error)
applyDefaultValues()
}
)
}
}
}

private fun applyExperiments(experiments: Map<String, Any>) {
// Aplicar experimentos na UI
findViewById<Button>(R.id.button).text = experiments["button_text"].toString()
findViewById<Button>(R.id.button).setBackgroundColor(
Color.parseColor(experiments["button_color"].toString())
)
findViewById<View>(R.id.banner).visibility =
if (experiments["show_banner"] as Boolean) View.VISIBLE else View.GONE
}

private fun applyDefaultValues() {
// Valores padrão quando há erro
findViewById<Button>(R.id.button).text = "Clique Aqui"
findViewById<Button>(R.id.button).setBackgroundColor(Color.parseColor("#FF0000"))
findViewById<View>(R.id.banner).visibility = View.GONE
}
}

Próximos Passos