기초다지기(1) - Broadcast Receiver

JongHyunSeo·2025년 2월 27일

Android 기초

목록 보기
1/7

Broadcast Receiver?

앱 또는 시스템에서 발생한 이벤트를 감지하고 처리할 수 있는 컴포넌트로, 일반적으로 Intent 객체를 사용하여 메세지를 수신한다. 해당 객체에는 처리할 이벤트에 대한 정보와 데이터를 담고 있다.

주로 비동기적으로 메세지를 주고받을때나, 앱이 실행 중이 아닌 상태에서 특정 이벤트를 감지하기 위해 사용된다.

언제 사용할까?

  • 배터리 상태 감지 : 배터리 부족, 충전 시작 등
  • 네트워크 상태 변화 : 와이파이 연결, 모바일 데이터 ON/OFF
  • 부팅 완료 감지 : 디바이스 재부팅
  • 화면 ON/OFF 감지 : 화면 ON/OFF
  • 앱 설치 / 삭제 감지 : 앱 설치 및 삭제
  • SMS 수신 : 문자 메세지 수신
  • 알람 이벤트 처리 : AlarmManager를 사용해 특정 시간에 전송

장점

  • 이벤트 기반 처리 : 이벤트 발생 시 자동으로 실행되기 때문에 비동기적으로 이벤트 감지 및 처리 가능
  • 리소스 절약 : 이벤트 기반이기 때문에 배터리와 리소스 절약 가능
  • 앱 실행 여부와 무관한 동작 : 정적으로 등록한 경우, 앱이 실행되지 않아도 특정 이벤트를 감지할 수 있음
  • 다른 앱과의 데이터 공유 가능 : 다른 앱이 보내는 Broadcast를 수신할 수 있어 앱간 통신 가능
  • 순서 제어 가능 : 우선순위에 따라 Broadcast를 순차적으로 실행할 수 있으며, 중간에 데이터 변경 가능

단점 및 주의점

  • 대량 이벤트 발생 시 성능 문제 : 빈번한 전송이 있을 경우, 불필요한 이벤트 처리로 성능이 저하될 수 있음
  • 등록 타입에 따른 동작 상이 : 동적으로 등록한 BroadcastReceiver는 앱이 실행 중일 때만 동작함
  • 보안 문제 : 공개 sendBroadcast() 사용 시 다른 앱이 가로챌 수 있어 LocalBroadcastManager또는 sendBroadcast(intent, permission) 이렇게 사용해야함
  • BroadcastReceiver는 일회성 이벤트 감지용으로 긴 작업을 수행하면 ANR이 발생할 수 있어, 긴 작업의 경우 IntentService와 함께 사용해야함
  • Android 8.0(API 26)부터 Mainfest에 등록된 Broadcast중 일부는 동작이 제한되었으며, registerReceiver()를 사용한 동적 등록이 필요함
  • Android 14부터는 Cache 상태인 앱에는 시스템 Broadcast가 즉시 전달되지 않을 수 있다.
    중요도가 낮은 BroadcastCache 상태에서 빠져나와야 전달되고, 중요도 높은 Broadcast를 수신한 경우에는 Cache 상태를 잠시 해제함

정적 등록 방식

앱이 실행중이지 않아도 이벤트 감지가 가능하다. 다만 정적 등록에 대한 제한사항이 존재한다.

사용 예시

Manifest

<!-- 시스템 혹은 다른 앱의 broadcast를 수신하는 경우에는,
     android:exported = "true" 설정이 필요하다.         -->
     
<receiver android:name=".BootReceiver"
    android:exported="false"> 
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>

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

BoostReceiver

class BootReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
            Log.d("BootReceiver", "디바이스 부팅 완료됨!")
        }
    }
}

동적 등록 방식

앱이 실행 중일 때만 동작하며, 앱 종료 시 Receiver 해제가 필요하다. 특정 Activity 또는 Service 내에서만 작동하도록 제한할 수 있다.

사용 예시

ex) 네트워크 상태 변경 감지

class MainActivity : AppCompatActivity() {
    private val networkReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            Log.d("NetworkReceiver", "네트워크 상태 변경됨!")
        }
    }

    override fun onResume() {
        super.onResume()
        val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        registerReceiver(networkReceiver, filter)
    }

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

registerReceiver()를 사용하여 실행 중일 때만 이벤트 감지
unregisterReceiver()로 해제하지 않으면 메모리 누수가 발생할 수 있어 주의 요망

LocalBroadcastManager (앱 내부)

앱 내부에서만 데이터를 주고받을 때 사용하며, 외부 앱의 Broadcast를 막아 보안성을 높일 수 있다.

사용 예시

val intent = Intent("com.example.ACTION_CUSTOM")
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)

BroadcastReceiver에서 감지

val receiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d("LocalBroadcast", "내부 이벤트 발생 감지됨!")
    }
}

val filter = IntentFilter("com.example.ACTION_CUSTOM")
LocalBroadcastManager.getInstance(context).registerReceiver(receiver, filter)

sendBroadcast()

모든 등록된 BoradcastReceiver에 동시에 비순차적으로 전달되며, 리시버가 다른 리시버에게 데이터를 넘기지 못하기 때문에 데이터 변경이 불가능하다.

주로 알람, 네트워크 변경 등 빠른 이벤트 전달이 필요한 경우 적합하다.

사용 예시

val intent = Intent("com.example.CUSTOM_ACTION")
sendBroadcast(intent)  // 여러 개의 리시버가 동시에 처리

BroadcastReceiver에서 감지

class MyReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d("MyReceiver", "Broadcast 수신됨!")
    }
}

다만 위처럼 그냥 사용하면 보안상 이슈가 있을 수 있어, sendBroadcast(Intent, String permission) 형태로 사용하여 특정 권한이 있는 앱만 수신 가능하도록 설정해 사용하는게 좀 더 바람직 하다.

사용법

val intent = Intent("com.example.SECURE_ACTION")
sendBroadcast(intent, "com.example.MY_CUSTOM_PERMISSION")

manifest

<permission
    android:name="com.example.MY_CUSTOM_PERMISSION"
    android:protectionLevel="signature"/>
<receiver android:name=".SecureReceiver">
    <intent-filter>
        <action android:name="com.example.SECURE_ACTION"/>
    </intent-filter>
    <permission android:name="com.example.MY_CUSTOM_PERMISSION"/>
</receiver>

sendOrderedBroadcast()

우선순위를 적용하여 순차적으로 전달할 수 있으며, 여러개의 BroadcastReceiver가 우선순위에 따라 순서대로 실행된다. 다음 리시버로 데이터를 넘길 수 있기 때문에(setResultData(), getResultData()) 데이터 변경이 가능하다.

해당 방식도 위와 동일하게 permission을 매개변수로 추가하여 보안을 강화할 수 있다.

주로 우선순위를 두고 특정 리시버에서 데이터를 조작해야할 경우 적합하다.

사용 예시

val intent = Intent("com.example.ORDERED_ACTION")
sendOrderedBroadcast(intent, null)

manifest 설정

<receiver android:name=".FirstReceiver" android:priority="2">
    <intent-filter>
        <action android:name="com.example.ORDERED_ACTION"/>
    </intent-filter>
</receiver>

<receiver android:name=".SecondReceiver" android:priority="1">
    <intent-filter>
        <action android:name="com.example.ORDERED_ACTION"/>
    </intent-filter>
</receiver>

첫번째 리시버

class FirstReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d("FirstReceiver", "FirstReceiver 실행됨!")
        setResultData("데이터 변경됨")  // 다음 리시버에게 데이터 전달 가능
    }
}

두번째 리시버

class SecondReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val data = getResultData()
        Log.d("SecondReceiver", "SecondReceiver 실행됨! 받은 데이터: $data")
    }
}
profile
공부는 꾸준하게

0개의 댓글