Communication between Fragment and Activity
Tags: android, kotlin, fragment, activity
Published: 2017-12-21 10:49:00

---
title: Communication between Fragment and Activity
published: true
description: 
tags: android, kotlin, fragment, activity
---

Original post https://github.com/onmyway133/blog/issues/108

There's always need for communication, right 😉 Suppose we have `OnboardingActivity` that has several `OnboardingFragment`. Each `Fragment` has a `startButton` telling that the onboarding flow has finished, and only the last `Fragment` shows this button.

Here are several ways you can do that

## 1. EventBus 🙄

Nearly all articles I found propose this https://github.com/greenrobot/EventBus, but I personally don't like this idea because components are loosely coupled, every component and broadcast can listen to event from a singleton, which makes it very hard to reason when the project scales

```kotlin
data class OnboardingFinishEvent()
```

```kotlin
class OnboardingActivity: AppCompatActivity() {
    override fun onStart() {
        super.onStart()

        EventBus.getDefault().register(this)
    }

    override fun onStop() {
        EventBus.getDefault().unregister(this)
        super.onStop()
    }
    
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onOnboardingFinishEvent(event: OnboardingFinishEvent) {
        // finish
    }
}
```

```kotlin
class OnboardingFragment: Fragment() {
    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        startButton.onClick {
            EventBus.getDefault().post(OnboardingFinishEvent())
        }
    }
}
```

Read more

- https://gunhansancar.com/ease-communication-between-activities-fragments-services/

## 2. Otto 🙄

This https://github.com/square/otto was deprecated in favor of RxJava and RxAndroid

## 3. RxJava 🙄

We can use simple `PublishSubject` to create our own [RxBus](https://android.jlelse.eu/super-simple-event-bus-with-rxjava-and-kotlin-f1f969b21003)

```kotlin
import io.reactivex.Observable
import io.reactivex.subjects.PublishSubject

// Use object so we have a singleton instance
object RxBus {
    
    private val publisher = PublishSubject.create<Any>()

    fun publish(event: Any) {
        publisher.onNext(event)
    }

    // Listen should return an Observable and not the publisher
    // Using ofType we filter only events that match that class type
    fun <T> listen(eventType: Class<T>): Observable<T> = publisher.ofType(eventType)
}
```

```kotlin
// OnboardingFragment.kt
startButton.onClick {
    RxBus.publish(OnboardingFinishEvent())
}
```

```kotlin
// OnboardingActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
        
    RxBus.listen(OnboardingFinishEvent::class.java).subscribe({
        // finish
    })
}
```

## 4. Interface

This is advised here [Communicating with Other Fragments](https://developer.android.com/training/basics/fragments/communicating.html#DefineInterface). Basically you define an interface `OnboardingFragmentDelegate` that whoever conforms to that, can be informed by the `Fragment` of events. This is similar to `Delegate` pattern in iOS 😉 

```kotlin
interface OnboardingFragmentDelegate {
    fun onboardingFragmentDidClickStartButton(fragment: OnboardingFragment)
}

class OnboardingFragment: Fragment() {
    var delegate: OnboardingFragmentDelegate? = null

    override fun onAttach(context: Context?) {
        super.onAttach(context)

        if (context is OnboardingFragmentDelegate) {
            delegate = context
        }
    }

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        startButton.onClick {
            delegate?.onboardingFragmentDidClickStartButton(this)
        }
    }
}
```

```kotlin
class OnboardingActivity: AppCompatActivity(), OnboardingFragmentDelegate {
    override fun onboardingFragmentDidClickStartButton(fragment: OnboardingFragment) {
        onboardingService.hasShown = true
        startActivity<LoginActivity>()
    }
}
```

## 5. ViewModel

We can learn from [Share data between fragments](https://developer.android.com/topic/libraries/architecture/viewmodel.html) to to communication between Fragment and Activity, by using a shared `ViewModel` that is scoped to the activity. This is a bit overkill

```kotlin
class OnboardingSharedViewModel: ViewModel() {
    val finish = MutableLiveData<Unit>()
}
```

```kotlin
class OnboardingActivity: AppCompatActivity(), OnboardingFragmentDelegate {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val viewModel = ViewModelProviders.of(this).get(OnboardingSharedViewModel::class.java)

        viewModel.finish.observe(this, Observer {
            startActivity<LoginActivity>()
        })
    }
}
```

Note that we need to call `ViewModelProviders.of(activity)` to get the same `ViewModel` with the `activity`

```kotlin
class OnboardingFragment: Fragment() {
    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val viewModel = ViewModelProviders.of(activity).get(OnboardingSharedViewModel::class.java)
        startButton.onClick({
            viewModel.finish.value = Unit
        })
    }
}
```

## 7. Lambda

Create a lambda in `Fragment`, then set it on `onAttachFragment`. It does not work for now as there is no `OnboardingFragment` in `onAttachFragment` 😢 

```kotlin
class OnboardingFragment: Fragment() {
    var didClickStartButton: (() -> Unit)? = null

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        startButton.onClick {
            didClickStartButton?.invoke()
        }
    }
}
```

```kotlin
class OnboardingActivity: AppCompatActivity() {
    override fun onAttachFragment(fragment: Fragment?) {
        super.onAttachFragment(fragment)

        (fragment as? OnboardingFragment).let {
            it?.didClickStartButton = {
                // finish
            }
        }
    }
}
```

## 8. Listener in Bundle 🙄

Read more

- https://medium.com/groupon-eng/from-fragments-to-activity-the-lambda-way-32c768c72aa9