Background
A common task in an Android application is to listen to system broadcasts and perform some action. For example you may want to listen to network connectivity changes and pause or resume some network communication depending on the network state.
Normally this involves implementing a BroadcastReceiver
then registering it with Context.registerReceiver
and unregistering it with Context.unregisterReceiver
in the appropriate life-cycle methods. While it is not a lot of code by itself, it can quickly become tiring to write and maintain if receivers are used many times throughout an application.
For this reason we can simplify the procedure significantly using a combination of Kotlin extension functions, RxAndroid and the Android Lifecycle-Aware Components library.
First steps
Our first step is to define a observeBroadcasts
extension function on Context, this will allow us to use it anywhere we have a Context
object available, be it in Activity
or in a Service
.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun Context.observeBroadcasts(action: String): Observable<Intent> { | |
return observeBroadcasts(IntentFilter(action)) | |
} | |
fun Context.observeBroadcasts(intentFilter: IntentFilter): Observable<Intent> { | |
val observable = Observable.create<Intent> { observer -> | |
val receiver = object : BroadcastReceiver() { | |
override fun onReceive(context: Context, intent: Intent) { | |
observer.onNext(intent) | |
} | |
} | |
observer.setDisposable(Disposables.fromRunnable { | |
unregisterReceiver(receiver) | |
}) | |
registerReceiver(receiver, intentFilter) | |
} | |
return observable | |
.subscribeOn(AndroidSchedulers.mainThread()) | |
.observeOn(AndroidSchedulers.mainThread()) | |
} |
Example usage in an Activity
:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private var connectivitySubscription: Disposable? = null | |
override fun onStart() { | |
super.onStart() | |
connectivitySubscription = observeBroadcasts(ConnectivityManager.CONNECTIVITY_ACTION) | |
.subscribe(this::onConnectivityChange) | |
} | |
override fun onStop() { | |
super.onStop() | |
connectivitySubscription?.dispose() | |
} | |
private fun onConnectivityChange(intent: Intent) { | |
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager | |
val isConnected = connectivityManager.activeNetworkInfo?.isConnected ?: false | |
Log.i("MainActivity", "isConnected = $isConnected") | |
} |
Adding lifecycle awareness
The previous example improved our code by removing the need to implement a BroadcastReceiver
and hiding the registration flow behind a standard Rx Observable
. We can however take one step further by utilizing the recently added LifecycleObserver
interface and related methods in the Android Architecture Components.
This new subscribeToBroadcastsOnLifecycle
is called only from Activity.onCreate
with no further interaction necessary.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun AppCompatActivity.subscribeToBroadcastsOnLifecycle(action: String, fn: (Intent) -> Unit) { | |
observeBroadcasts(action).subscribeOnLifecycle(lifecycle, fn) | |
} | |
fun <T> Observable<T>.subscribeOnLifecycle(lifecycle: Lifecycle, fn: (T) -> Unit) { | |
val lifecycleObserver: LifecycleObserver = object : LifecycleObserver { | |
private var subscription: Disposable? = null | |
@OnLifecycleEvent(Lifecycle.Event.ON_START) | |
fun onStart() { | |
subscription = subscribe(fn) | |
} | |
@OnLifecycleEvent(Lifecycle.Event.ON_STOP) | |
fun onStop() { | |
subscription?.dispose() | |
} | |
} | |
lifecycle.addObserver(lifecycleObserver) | |
} |
Example usage in an Activity
:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_main) | |
subscribeToBroadcastsOnLifecycle(ConnectivityManager.CONNECTIVITY_ACTION, this::onConnectivityChange) | |
} | |
private fun onConnectivityChange(intent: Intent) { | |
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager | |
val isConnected = connectivityManager.activeNetworkInfo?.isConnected ?: false | |
Log.i("MainActivity", "isConnected = $isConnected") | |
} |
End notes
- Typical blog-code disclaimer: The code snippets above are only intended as inspiration and do not include all recommended error handling code.
subscribeToBroadcastsOnLifecycle
currently only usesonStart
andonStop
but should be easy to extend to support any appropriate lifecycle method pairs.