Broadcast Receiver in Android: Enhancing Connectivity and Security

Abhishek Pathak
7 min readSep 16, 2022

--

In today’s digital era, the internet has become an integral part of our lives, leading to a growing dependence on online connectivity. Consequently, most of our applications rely on internet access to retrieve the latest updates. Let’s consider a scenario where I have developed an application that also requires an internet connection. However, there may be instances where the user loses their internet connection precisely when they request the retrieval of new updates.

What will happen in this Scenario? any guess???

It very obvious that it will lead to crash if not handled properly or it may give some exception if handled properly but in both way our application is not user friendly. correct???

So what we can give a solution to make our app user friendly?

Why does broadcast receiver do in android development?

Broadcast receiver is a beautiful solution for this problem as well as it will solves many other similar problems like

  • Battery is low for video call, example — WhatsApp using this feature
  • airplane mode off/on and you want to make a call — Android apps using this feature
  • Enable the camera before scanning for payments — All Payement apps using this feature too like Paytm, PayPal etc.

What is Broadcast receiver?

A Broadcast Receiver is an essential component of an Android application. It serves as a listener that receives various events from the device. These events can range from system-level events to custom events specific to the application.

Ways to register a broadcast receivers :-

  • Static broadcast receivers : These receivers are added into android manifest file and it works even if app got closed. This receiver works only pre Oreo devices / Below Android Version 8.
<receiver android:name=".AirplaneMOdeDetector"/>

Dynamic broadcast receivers : These receivers added into kotlin/Java classes and it work only when app is active or minimized. It is introduced in Android Oreo.

What is intent filter?

    /*
Intent filter helps to determine which apps wants to receive which intents,
Since here we want to respond to change of airplane mode
*/
IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED).also {
registerReceiver(airplaneModeDetector, it)
}

Use -cases of Custom Broadcast receivers

  • Alarm application — Whenever you keep an alarm, you leave that application or simple terms, close that application. Still, at that particular time, you receive the ring of your alarm.
  • Remainder app — Whenever your device has insufficient charge left, your device notifies you to keep charge or on battery saver mode.

Dis-advantages of broadcast receivers

  1. It is battery draining component.
  2. It is heavy component so if not unregistered/ handled properly it may lead to memory leaks.

How do we implement broadcast receiver?

Let’s implement an app which will detect the airplane mode enabled/ disabled simply.

Step 1 : Make a receiver for changes on airplane mode off/on

class AirplaneMOdeDetector : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent) {
val airplaneMOdeEnabled = intent.getBooleanExtra("state",
false)

if (airplaneMOdeEnabled) {
makeToast(context, "Airplane mode enabled!!")
} else {
makeToast(context, "Airplane mode disabled!!")
}
}

private fun makeToast(context: Context, s: String) {
Toast.makeText(context, s, Toast.LENGTH_LONG).show()
}
}

Step 2: Register/ unregister into your activity file

class MainActivity : AppCompatActivity() {
private lateinit var airplaneModeDetector: AirplaneModeDetector
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
}
private fun initView() {
airplaneModeDetector = AirplaneModeDetector()
/*
Intent filter helps to determine which apps wants to receive which intents,
Since here we want to respond to change of airplane mode
*/
IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED).also {
registerReceiver(airplaneModeDetector, it)
}
}
override fun onPause() {
super.onPause()
unregisterReceiver(airplaneModeDetector)
}
}

How to unregister a broadcast receiver?

override fun onPause() {
super.onPause()
unregisterReceiver(airplaneModeDetector)
}

LocalBroadcastReceiver

Broadcast receiver allows you to send and receive events for the app as well it is available to other apps as well that’s not good for security concern and definitely a bad for coding practices, Ideally you should go for Local broadcast receiver if it is not system event.

Implementation of Local broadcast manager

Step 1: Create an class to receive local events triggred by local brodacast manager

inner class MyLocalBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
"PlayGame" -> {
val result = intent.getStringExtra("data")
Toast.makeText(context, result.toString(), Toast.LENGTH_SHORT).show()
binding.txtData.text = result.toString()
}
else -> {
Toast.makeText(context, "Action not found!!", Toast.LENGTH_SHORT).show()
}
}
}
}

Step 2: how to send broadcast and register a local broadcast manager

private fun localBroadcastReceiver() {
binding.buttonSendLocalBroadcast.setOnClickListener {
localBroadcastManager = LocalBroadcastManager.getInstance(this)
myLocalBroadcastReceiver = MyLocalBroadcastReceiver()
localBroadcastManager.registerReceiver(
myLocalBroadcastReceiver,
IntentFilter("PlayGame")
)
//here we are creating our own action intent
val localIntent = Intent("PlayGame")
// adding data to the intent
.putExtra("data", "PUBG")
//sending our own broadcast
localBroadcastManager.sendBroadcast(localIntent)
}
}

Here is the full source code :)

class MainActivity : AppCompatActivity() {
private lateinit var airplaneModeDetector: AirplaneModeDetector
private lateinit var localBroadcastManager: LocalBroadcastManager
private lateinit var binding: ActivityMainBinding
private lateinit var myLocalBroadcastReceiver: MyLocalBroadcastReceiver

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
globalBroadcastReceiver()
localBroadcastReceiver()
}
private fun globalBroadcastReceiver() {
airplaneModeDetector = AirplaneModeDetector()
/*
Intent filter helps to determine which apps wants to receive which intents,
Since here we want to respond to change of airplane mode
*/
IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED).also {
registerReceiver(airplaneModeDetector, it)
}
}
private fun localBroadcastReceiver() {
binding.buttonSendLocalBroadcast.setOnClickListener {
localBroadcastManager = LocalBroadcastManager.getInstance(this)
myLocalBroadcastReceiver = MyLocalBroadcastReceiver()
localBroadcastManager.registerReceiver(
myLocalBroadcastReceiver,
IntentFilter("PlayGame")
)
//here we are creating our own action intent
val localIntent = Intent("PlayGame")
// adding data to the intent
.putExtra("data", "PUBG")
//sending our own broadcast
localBroadcastManager.sendBroadcast(localIntent)
}
}
override fun onDestroy() {
super.onDestroy()
try {
//Register or UnRegister your broadcast receiver here
if (this::airplaneModeDetector.isInitialized) {
unregisterReceiver(airplaneModeDetector)
}
if (this::myLocalBroadcastReceiver.isInitialized) {
//As we are registering to the local broadcast manager so ideally we should
//unregister using local broadcast manager
localBroadcastManager.unregisterReceiver(myLocalBroadcastReceiver)
}
} catch (e: IllegalArgumentException) {
e.printStackTrace()
}
}
inner class MyLocalBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
"PlayGame" -> {
val result = intent.getStringExtra("data")
Toast.makeText(context, result.toString(), Toast.LENGTH_SHORT).show()
binding.txtData.text = result.toString()
}
else -> {
Toast.makeText(context, "Action not found!!", Toast.LENGTH_SHORT).show()
}
}
}
}
}

Use Case 2: Internet check using broadcast receiver

Step 1: Creating the Broadcast Receiver

  1. Create a new Kotlin file for the broadcast receiver. Right-click on the package where you want to place the file, and select New -> Kotlin File/Class.
  2. Name the file ConnectivityReceiver or any other suitable name.
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager

class ConnectivityReceiver : BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent) {
if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) {
val isConnected = isNetworkConnected(context)
// Handle the network state change (connected or disconnected) here
}
}

private fun isNetworkConnected(context: Context): Boolean {
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val networkInfo = connectivityManager.activeNetworkInfo
return networkInfo != null && networkInfo.isConnected
}
}

Step 2: Registering the Broadcast Receiver
Open the AndroidManifest.xml file and add the following code inside the <application> tag to register the receiver.

<receiver
android:name=".ConnectivityReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>

don’t forgot to add this into manifest

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Step 4: Handling Connectivity Changes To respond to changes in internet connectivity, you can either create a callback mechanism within the broadcast receiver or use LocalBroadcastManager to communicate with other components in your app. Here, we will demonstrate using a callback mechanism.

  1. Define an interface for the callback in your activity or fragment where you want to handle connectivity changes.
interface ConnectivityListener {
fun onNetworkConnectionChanged(isConnected: Boolean)
}

In the activity or fragment, implement the ConnectivityListener interface and override the onNetworkConnectionChanged method.

class YourActivity : AppCompatActivity(), ConnectivityListener {

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

// Register the activity as a listener for connectivity changes
val connectivityReceiver = ConnectivityReceiver()
connectivityReceiver.setConnectivityListener(this)
}

override fun onNetworkConnectionChanged(isConnected: Boolean) {
if (isConnected) {
// Internet connection is available, handle the scenario
} else {
// No internet connection, handle the scenario
}
}
}

This approach enables your app to respond to network changes dynamically, providing a smoother user experience. By implementing the broadcast receiver and handling the connectivity changes effectively, you can ensure that your app gracefully adapts to different network scenarios. Always consider user feedback and edge cases while designing network-dependent features to create a more robust application. Happy coding!

Ordered broadcast

Use Case: Managing Device Boot Sequence

Imagine a scenario where multiple applications need to perform initialization tasks when the device boots up. For example, a security application might need to initialize first to ensure that the device is secure before other applications start their tasks. An ordered broadcast can be used to ensure that the security application gets the boot completed broadcast first and performs its tasks before any other apps.

SecurityReceiver.kt:

package com.example.app

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log

class SecurityReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
// Security initialization tasks
Log.d("SecurityReceiver", "Security initialization tasks completed.")

// Propagate the broadcast to the next receiver
resultCode = android.app.Activity.RESULT_OK
}
}
package com.example.app

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log

class OtherAppReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
// Other app initialization tasks
Log.d("OtherAppReceiver", "Other app initialization tasks completed.")
}
}

OtherAppReceiver.kt:

package com.example.app

import android.app.Application
import android.content.IntentFilter
import android.os.Build

class MyApplication : Application() {

private val securityReceiver = SecurityReceiver()
private val otherAppReceiver = OtherAppReceiver()

override fun onCreate() {
super.onCreate()

val securityIntentFilter = IntentFilter().apply {
addAction(Intent.ACTION_BOOT_COMPLETED)
priority = 100 // Higher priority
}

val otherAppIntentFilter = IntentFilter().apply {
addAction(Intent.ACTION_BOOT_COMPLETED)
priority = 50 // Lower priority
}

// Register the receivers programmatically
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// For API level 26 and above, we need to register receivers dynamically in some cases
registerReceiver(securityReceiver, securityIntentFilter)
registerReceiver(otherAppReceiver, otherAppIntentFilter)
}
}

override fun onTerminate() {
super.onTerminate()
unregisterReceiver(securityReceiver)
unregisterReceiver(otherAppReceiver)
}
}

Update AndroidManifest.xml

Update the AndroidManifest.xml to specify the custom Application class and request the RECEIVE_BOOT_COMPLETED permission.

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

Testing the Ordered Broadcast

To test this implementation, you can reboot the device or use an ADB command to simulate the BOOT_COMPLETED broadcast:

adb shell am broadcast -a android.intent.action.BOOT_COMPLETED

n this use case, the ordered broadcast ensures that the security application initializes first when the device boots up, followed by other applications. This ordering is critical for maintaining the security and integrity of the device before other tasks are performed.

Thank you :)

Thank you for reading my article. I really appreciate your response.

Clap if this article helps you. If I got something wrong, please comment for improve.
let’s connect on
Linkedin , GitHub

--

--