paint-brush
8 ways to communicate between Fragment and Activity in Android appsby@onmyway133
16,623 reads
16,623 reads

8 ways to communicate between Fragment and Activity in Android apps

by Khoa PhamAugust 16th, 2018
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

There’s always need for communication, right 😉 Suppose we have <code class="markup--code markup--p-code">OnboardingActivity</code> that has several <code class="markup--code markup--p-code">OnboardingFragment</code>. Each <code class="markup--code markup--p-code">Fragment</code> has a <code class="markup--code markup--p-code">startButton</code> telling that the onboarding flow has finished, and only the last <code class="markup--code markup--p-code">Fragment</code> shows this button.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - 8 ways to communicate between Fragment and Activity in Android apps
Khoa Pham HackerNoon profile picture

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. Any feedback or improvement suggestions are more than welcome.

EventBus

Nearly all articles I found propose this 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

data class OnboardingFinishEvent()



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  
}  

}



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

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

}

Read more about this approach

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

Otto

Otto from square/otto said to be an enhanced Guava-based event bus with emphasis on Android support. However it is deprecated

This project is deprecated in favor of RxJava and RxAndroid. These projects permit the same event-driven programming model as Otto, but they’re more capable and offer better control of threading.

RxJava

We can use simple PublishSubject to create our own RxBus


import io.reactivex.Observableimport io.reactivex.subjects.PublishSubject


// Use object so we have a singleton instanceobject 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)  

}




// OnboardingFragment.ktstartButton.onClick {RxBus.publish(OnboardingFinishEvent())}



// OnboardingActivity.ktoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)

RxBus.listen(OnboardingFinishEvent::class.java).subscribe({  
    // finish  
})  

}

Interface

This is advised here Communicating with Other Fragments. 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 😉



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)  
    }  
}  

}






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

ViewModel

We can learn from Share data between fragments to to communication between Fragment and Activity, by using a shared ViewModel that is scoped to the activity. This is a bit overkill



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




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



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  
    })  
}  

}

Lambda

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


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

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

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

}



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

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

}

Listener in Bundle

Another proposed way is through listener in bundle, the below post gives more insight into this approach


From Fragments to Activity: the Lambda Way_Activities have their lifecycle, and the same goes forFragments. Hence, communication from Fragments to Activities is…_medium.com

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

While you are here, you may like my other posts

If you like this post, consider visiting my other articles and apps 🔥