지금까지 개발을 하면서 BroadcastReceiver를 사용해본 적이 별로 없는 것 같다. 분명 4대 컴포넌트인데 이를 제대로 알지 못하는 것 같아 간단히 적용해보는 방법을 알아보려고 한다.
사실 큰 작업은 아니고 브로드캐스트가 제대로 호출이 되는지 로그를 통해 알아보려고 한다.
BroadcastReceiver는 안드로이드에서 시스템이나 다른 앱이 발생시키는 브로드캐스트 이벤트를 감지하고 처리하는 컴포넌트입니다.
브로드캐스트란, 예를 들어 "기기가 부팅되었다", "충전기가 연결되었다", "배터리 상태가 변경되었다" 같은 시스템의 상태 변화나 앱 내부에서 전송하는 메시지를 의미합니다.
이러한 이벤트가 발생하면, 안드로이드 시스템은 해당 이벤트를 전파하고, 이를 수신하도록 등록된 BroadcastReceiver가 호출됩니다.
class TestBroadCastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.e("Chan2", "start receiver type: ${intent?.type}, action : ${intent?.action}")
when(intent?.action) {
Intent.ACTION_BATTERY_CHANGED -> {
Log.d("Chan2", "Action Battery Charged")
}
Intent.ACTION_BOOT_COMPLETED -> {
Log.d("Chan2", "Action Boot Complete")
}
Intent.ACTION_SCREEN_ON -> {
Log.d("Chan2", "Action Screen On")
}
Intent.ACTION_SCREEN_OFF -> {
Log.d("Chan2", "Action Screen Off")
}
}
}
}
테스트용 브로드캐스트를 만들고 위와같이 4가지 경우에 대해서 브로드캐스트 동작 로그를 출력하도록 구현했다.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!--부트 권한이 있어야 앱이 재실행 됐을 때 로그가 찍힘-->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application>
<receiver
android:name=".broadcastreceiver.TestBroadCastReceiver"
android:enabled="true"
android:exported="true">
<!--관련된 기능들을 사용할 것들을 명시한다.-->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.BATTERY_CHANGED" />
<action android:name="android.intent.action.SCREEN_ON" />
<action android:name="android.intent.action.SCREEN_OFF" />
</intent-filter>
</receiver>
</application>
</manifest>
class MainActivity : ComponentActivity() {
//브로드캐스트 생성
private val testBroadCastReceiver = TestBroadCastReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
//filter 추가하기
val filter = IntentFilter().apply {
addAction(Intent.ACTION_BATTERY_CHANGED)
addAction(Intent.ACTION_BOOT_COMPLETED)
addAction(Intent.ACTION_SCREEN_ON)
addAction(Intent.ACTION_SCREEN_OFF)
}
Log.e("Chan2", "onCreate")
//브로드캐스트 리시버 등록하기
ContextCompat.registerReceiver(this, testBroadCastReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
setContent {
BroadcastReceiverExTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
override fun onDestroy() {
super.onDestroy()
//앱이 종료됐을 때 리시버 종료하기
this.unregisterReceiver(testBroadCastReceiver)
}
}
이제 앱을 실행해보면 아래 로그와 같이 각각의 상황에서 로그가 찍히는 것을 볼 수 있다.
앱을 재실행 하는 것부터 시작해서 -> 앱 실행 -> 스크린 닫기 -> 스크린 열기 등으로 실행했을 때 아래와 같은 로그가 출력된 것을 볼 수 있다.

사실 위에 방법은 xml 기반일 때 방식이거나 모든 화면에서 브로드캐스트를 등록할 때 사용하는 방식이다. 최근에는 Compose UI를 사용하기 때문에 해당 화면에서 브로드캐스트 리시버를 등록하는 방법은 아래와 같이 작성하면 된다.
class MainActivity : ComponentActivity() {
private val testBroadCastReceiver = TestBroadCastReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
val filter = IntentFilter().apply {
addAction(Intent.ACTION_BATTERY_CHANGED)
addAction(Intent.ACTION_BOOT_COMPLETED)
addAction(Intent.ACTION_SCREEN_ON)
addAction(Intent.ACTION_SCREEN_OFF)
}
val context = LocalContext.current
LifecycleStartEffect(true) {
Log.e("Chan2","LifecycleStartEffect")
ContextCompat.registerReceiver(context, testBroadCastReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
onStopOrDispose {
Log.e("Chan2","onStopOrDispose")
context.unregisterReceiver(testBroadCastReceiver)
}
}
BroadcastReceiverExTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
해당 코드는 android 브로드캐스트 공식 문서에 있는 것을 그대로 적용했다.
하지만 문제점이 있었는데 바로 Screen ON/OFF를 감지하지 못한다는 것이다.
확인해 보니 스크린을 닫았을 때 onStopOrDispose가 호출되서 이미 브로드캐스트가 해제되었기 때문이다.
Screen On/OFF같은 경우에는 applilcation이나 Acitivity 범위 내에서 동작을 해야 하는 것이 맞기 때문에 동작이 되지 않는다. 심지에 Composable안에서는 DESTROY를 감지할 수 없어서 해제하는 방법이 존재하지 않는다.
그래서 웬만하면은 activity나 application에서 등록과 해제를 하는 것이 좋아보인다.
여기서 궁금한것은 브로드캐스트에는 Hilt를 사용할 수 없는데 Boot Complete를 호출 했을 때 서버에 있는 데이터를 받아와 어떤 처리를 할 수 있는지가 궁금했다.(뭐 그냥 보여주는 것이 아닌 Room에 저장같은 용도로)
직접 구현한 것은 아니지만 Workmanager와 함께 구현이 가능하다고 한다. Workmanager는 Hilt를 사용할 수 있기 때문에 repository를 inject할 수 있고 서버 호출도 가능하기 때문이다. 대략적인 흐름은 아래와 같다.
이것도 궁금해서 찾거나 gpt한테 물어봤는데 매니페스트 등록은 app모듈에 하는 것이 좋다고 하며, 전체적인 이벤트 수신 또한 app모듈에 동작하는 것을 추천한다고 말하기는 한다.
다만 특정 화면에서 동작하는 브로드캐스트일 경우에는 해당 feature 모듈 내에 구현해도 괜찮다고 얘기는 해서 이는 각자 프로젝트 상황에 따라 적절히 위치하는 것이 좋아 보인다