[4대 컴포넌트] Broadcast receivers

dwjeong·2023년 9월 1일
1

안드로이드

목록 보기
3/28
post-custom-banner

🔎 브로드캐스트 리시버란?

안드로이드 시스템 및 다른 안드로이드 앱에서 브로드캐스트 메시지를 주고받을 수 있음.

예시
시스템은 충전중이거나 배터리 부족, 사진이 캡쳐되었음을 알리는 브로드캐스트를 시작하고 리시버가 이를 수신한다.

앱이 특정 브로드캐스트를 수신하도록 등록할 수 있고, 브로드캐스트가 전송되면 시스템은 특정 유형의 브로드캐스트 수신을 등록한 앱에게 자동으로 브로드캐스트를 라우팅함.

유저 플로우가 아닌 앱 전반에서 메시징 시스템으로 사용할 수 있음.

브로드캐스트 메시지는 발생한 이벤트를 식별하는 액션 문자열이 포함된 인텐트 객체로 래핑됨.
(ex: android.intent.action.AIRPLANE_MODE)


1. 브로드 캐스트 수신

브로드 캐스트를 수신하는데에는 매니페스트에 리시버를 등록하거나 context에 리시버를 등록하는 두 가지 방법이 있다.

💡 매니페스트에 등록

매니페스트에서 브로드캐스트 리시버를 선언하면 브로드캐스트가 전송될 때 시스템이 앱을 실행함.

  1. 매니페스트에 <receiver> 요소를 지정.
    인텐트 필터는 브로드캐스트 액션 지정.
<receiver android:name=".MyBroadcastReceiver" android:exported="false">
    <intent-filter>
        <action android:name="APP_SPECIFIC_BROADCAST" />
    </intent-filter>
</receiver>
  1. BroadcastReceiver를 서브클래스로 만들고 onReceive(Context, Intent)를 구현함.
private const val TAG = "MyBroadcastReceiver"

class MyBroadcastReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        StringBuilder().apply {
            append("Action: ${intent.action}\n")
            append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n")
            toString().also { log ->
                Log.d(TAG, log)

                val binding = ActivityNameBinding.inflate(layoutInflater)
                val view = binding.root
                setContentView(view)

                Snackbar.make(view, log, Snackbar.LENGTH_LONG).show()
            }
        }
    }
}

시스템 패키지 관리자가 앱이 설치될때 리시버를 등록함. 리시버는 앱에 대한 별도의 진입점이 되어 앱이 실행중이 아닌 경우 시스템에서 앱을 시작하고 브로드캐스트를 전달할 수 있음.


💡 Context에 등록

컨텍스트에 등록된 리시버는 컨텍스트가 유효할 동안 브로드캐스트를 수신할 수 있다. 예를 들어 액티비티에 리시버를 등록할 경우 액티비티가 파괴되지 않는 이상 브로드캐스트를 수신하며 앱 컨텍스트에 등록하는 경우 앱이 실행중일 동안 브로드캐스트를 수신한다.

  1. 앱 모듈 수준 빌드 파일에 AndroidX 코어 라이브러리 버전 1.9.0 이상을 포함한다.

  2. BroadcastReceiver 인스턴스를 생성한다.

val br: BroadcastReceiver = MyBroadcastReceiver()
  1. 인텐트 필터 인스턴스를 생성한다.
val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
  1. 리시버가 시스템이나 유저가 소유한 다른 앱에서 보낸 브로드캐스트를 수신하고자 한다면 RECEIVER_EXPORTED 플래그를, 내 앱에서 보낸 브로드캐스트만 수신하고자 한다면 RECEIVER_NOT_EXPORTED 플래그를 사용한다.
val listenToBroadcastsFromOtherApps = false
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
    ContextCompat.RECEIVER_EXPORTED
} else {
    ContextCompat.RECEIVER_NOT_EXPORTED
}
  1. registerReceiver()를 통해 리시버를 등록한다.
ContextCompat.registerReceiver(context, br, filter, receiverFlags)
  1. 더 이상 리시버가 필요하지 않거나 컨텍스트가 유효하지 않은 경우 리시버 등록을 취소해야 함. 브로드캐스트 수신을 중지하려면 unregisterReceiver(android.content.BroadcastReceiver)를 호출.


    액티비티 컨텍스트를 사용하여 onCreate(Bundle)에 리시버를 등록한 경우 onDestroy()에서 리시버 해제.
    onResume()에 등록한 경우 리시버를 여러번 등록하지 않도록 onPause()에서 리시버 해제.
    onSaveInstanceState(Bundle)에서 리시버 해제하지 말 것. (다시 호출되지 않음)


2. 프로세스에 미치는 영향

포그라운드 프로세스는 리시버의 onReceive() 메소드를 실행.

브로드캐스트 리시버는 onReceive() 이후 비활성화 됨.
프로세스가 매니페스트에 선언된 리시버만 호스트하는 경우 시스템은 다른 프로세스에 리소스를 제공하기 위해 onReceive() 이후에 해당 프로세스를 종료할 수 있음.

따라서 브로드캐스트 리시버는 장기 실행 백그라운드 스레드를 시작해서는 안됨. 프로세스를 계속 유지하기 위해서는 JobScheduler를 이용하여 시스템이 프로세스가 계속 작동중임을 알 수 있게 해야함.

3. 브로드캐스트 전송

sendOrderedBroadcast(Intent, String)

한 번에 하나의 리시버에게 브로드캐스트를 전송. 리시버의 실행 순서는 일치하는 인텐트 필터의 android:priority 속성으로 제어하며 같은 우선순위를 가진 리시버는 임의의 순서로 실행됨.

sendBroadcast(Intent)

정의되지 않은 순서로 모든 리시버에게 브로드캐스트를 보냄. (Normal Broadcast)
효율적인 방법이지만 리시버가 다른 리시버의 결과를 읽을 수 없고 브로드캐스트에서 수신한 데이터를 전파할 수 없으며 브로드캐스트를 중단할 수 없는 단점이 있음.

Intent().also { intent ->
    intent.setAction("com.example.broadcast.MY_NOTIFICATION")
    intent.putExtra("data", "Nothing to see here, move along.")
    sendBroadcast(intent)
}

브로드캐스트 메시지는 인텐트 객체로 래핑. putExtra(String, Bundle)로 추가 정보 첨부 가능.

4. 퍼미션으로 브로드캐스트 제한

퍼미션을 이용하여 특정 권한을 가진 앱들로 브로드캐스트를 제한할 수 있음.

  • 퍼미션을 사용하여 전송
    sendBroadcast(Intent, String) 또는 sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) 를 호출할 때 권한 파라미터를 지정할 수 있음. 해당 퍼미션을 요청한 리시버만 브로드캐스트 수신 가능.
sendBroadcast(Intent(BluetoothDevice.ACTION_FOUND),
              Manifest.permission.BLUETOOTH_CONNECT)
  • 퍼미션을 이용하여 수신

    매니페스트에 리시버 등록
<receiver android:name=".MyBroadcastReceiver"
          android:permission="android.permission.BLUETOOTH_CONNECT">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_FOUND"/>
    </intent-filter>
</receiver>

또는 컨텍스트에 리시버 등록

var filter = IntentFilter(Intent.ACTION_FOUND)
registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null)

그리고 브로드캐스트를 보내는 앱과 수신하는 앱에서 다음과 같이 권한을 설정해줘야함.

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
post-custom-banner

1개의 댓글

comment-user-thumbnail
2024년 7월 12일

좋은 글 감사합니다~

답글 달기