티스토리 뷰
Broadcast와 BroadcastReceiver의 개념
안드로이드에서는 특정 이벤트가 발생할 때 Broadcast가 전송된다.
특정 Broadcast를 수신하도록 BroadcastReceiver를 등록할 수 있다.
간단히 말해서 어떤 이벤트를 구독하고, 그 이벤트가 발생할 때마다 구독자들에게 알리는 옵저버 패턴이라고 할 수 있다.
여기서 '알림'이 Broadcast, '구독자'가 BroadcastReceiver가 된다.
예를 들어 사용자가 휴대폰을 재부팅시키면, 부팅 이벤트를 등록한 BroadcastReceiver에게 Broadcast를 보낸다.
브로드캐스트의 종류는 상수로 표현되어 있는데,
어떠한 종류가 있는지는 아래 링크 공식문서에서 확인할 수 있다.
https://developer.android.com/reference/android/content/Intent#constants_1
BroadcastReceiver 생성 방법
1. 정적 리시버
리시버를 생성하는 방법에는 정적 리시버, 동적 리시버 두 가지 방법이 있다.
먼저 정적 리시버는 manifest안에 리시버를 등록하는 것이다.
앱이 설치되자마자 리시버가 등록되기 때문에, 별도로 리시버를 해제할 수 없다.
또한 앱을 실행하지 않을 경우에도 리시버가 실행된다.
그래서 전력 소모 문제로 API 26 이상에서는 정적 리시버를 선언하지 않도록 변경했다.
아직 정적 리시버가 가능한 브로드캐스트들이 있는데, 아래 링크를 참고하자.
https://developer.android.com/guide/components/broadcast-exceptions?hl=ko
<receiver android:name=".MyBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
</intent-filter>
</receiver>
기기가 부팅될 때, 입력 방식이 바뀌었을 때 MyBroadcastReceiver라는 리시버에게 브로드캐스트가 전달된다.
2. 동적 리시버
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 처리할 작업 작성
}
}
BroadcastReceiver 클래스를 상속받고, onReceive() 함수를 구현한다.
onReceive()는 리시버가 브로드캐스트를 받을 때 호출된다.
동적 리시버는 정적 리시버와 반대로, 앱이 실행되고 있을 때만 실행된다.
onReceive() 호출 동안에만 리시버 객체가 유효하고, onReceive()의 코드가 끝나면 리시버 객체는 활성화되지 않는다.
브로드캐스트 등록/해제 방법 (동적 리시버)
1. BroadcastReceiver의 인스턴스 생성
val br: BroadcastReceiver = MyBroadcastReceiver()
만들어준 동적 리시버 클래스의 인스턴스를 생성한다.
2. Broadcast 수신 등록하기
val filter = IntentFilter().apply {
addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)
}
registerReceiver(br, filter)
IntentFilter를 통해서 리시버가 어떤 액션을 구독할지 지정하고,
registerReceiver()를 통해 리시버가 Broadcast를 수신받을 수 있게 등록한다.
registerReceiver()에는 리시버와 인텐트 필터가 인자로 들어간다.
등록된 리시버는 등록된 곳의 Context가 살아있는 동안만 Broadcast를 수신한다.
예를 들어 Activity 내에서 등록하면, Activity가 활성화되어 있는 동안만 수신하며,
Application 내에서 등록하면, 앱이 실행되는 동안 수신한다.
3. Broadcast 수신 해제하기
unregisterReceiver(br)
unregisterReceiver()를 통해 Broadcast의 수신을 중지한다.
Activity의 onCreate()에 리시버를 등록했다면 onDestroy()에서 해제하고,
onResume()에 등록했다면 onPause()에서 해제해야 한다.
수신자가 여러 번 등록되지 않아 불필요한 오버헤드를 줄일 수 있기 때문이다.
리시버에게 브로드캐스트 전송하기
Intent action에 지정된 브로드캐스트 상수 외에도,
개발자가 직접 브로드캐스트 유형을 지정하고 Broadcast를 전송할 수 있다.
<receiver android:name=".TestBroadcastReceiver">
<intent-filter>
<action android:name="com.example.myapplication.SEND_BROADCAST"/>
</intent-filter>
</receiver>
예를 들어 TestBroadcastReceiver라는 리시버가SEND_BROADCAST라는 action을 받을 수 있다고 하자.
sendBroadcast(Intent().apply {
setAction("com.example.myapplication.SEND_BROADCAST")
putExtra("data", "Notice me senpai!")
})
intent에 SEND_BROADCAST라는 action을 담았다.
또한 putExtra()를 통해 intent에 추가 데이터를 담았다.
이 intent를 sendBroadcast()의 인자로 전달해 Broadcast를 전송했다.
그러면 SEND_BROADCAST라는 action을 구독하고 있는 모든 리시버들은 활성화가 될 것이다.
방금 manifest에 정적 리시버로 등록한 TestBroadcastReceiver도 SEND_BROADCAST를 구독하고 있으므로,
TestBroadcastReceiver의 onReceive() 함수가 호출될 것이다.
* sendBroadcast(Intent)
모든 리시버에게 랜덤한 순서로 Broadcast를 전송한다. (일반적인 브로드캐스트)
가장 효율적인 브로드캐스트지만, 제한 사항이 몇가지 있다.
리시버가 다른 리시버의 결과를 읽거나, Broadcast로부터 받은 데이터를 전달하거나, Broadcast를 중단할 수 없다.
* sendOrderedBroadcast(Intent, String)
하나의 리시버에 Broadcast를 전송한다.
리시버는 차례로 실행되기 때문에 다음 리시버로 전달하거나 Broadcast를 중단하여 다른 리시버로 전달되지 않게 할 수 있다.
Broadcast를 수신하는 리시버의 순서는 <intent-filter>의 android:priority>로 지정한다.
여기서 인자로 들어가는 String은 권한을 나타내는데, 바로 뒤에 설명하겠다.
* LocalBroadcastManager.sendBroadcast(Intent)
브로드캐스트를 보내는(발신자) 앱과 동일한 앱에 있는 리시버에게만 브로드캐스트를 전송한다.
앱 간 브로드캐스트를 전송할 필요없는 경우 사용한다.
프로세스 간 통신이 필요없기 때문에 효율적이고, 다른 앱이 브로드캐스트를 수신/전송할 때 발생하는 보안 문제도 해결된다.
권한을 사용하여 브로드캐스트 제한하기
권한을 요청하거나 권한을 부여받은 앱만 브로드캐스트를 전송하거나,
권한을 요청하거나 권한을 부여받은 앱만 브로드캐스트를 받을 수 있도록 제한할 수 있다.
브로드캐스트 전송
sendBroadcast(Intent("com.example.NOTIFY"), Manifest.permission.SEND_SMS)
SEND_SMS 권한을 인자로 전달하여 브로드캐스트를 전송하는 코드다.
<uses-permission android:name="android.permission.SEND_SMS"/>
해당 브로드캐스트를 수신하려면, manifest에 위 코드를 추가해 권한을 요청해야 한다.
브로드캐스트 수신
var filter = IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null)
이러한 동적 리시버가 있다고 가정하자. 리시버를 등록할 때, SEND_SMS 권한을 인자로 전송했다.
(또한 <receiver> 안에 android:permission 속성으로 권한을 지정한 정적 리시버)
<uses-permission android:name="android.permission.SEND_SMS"/>
위 코드처럼 발신자 앱이 권한을 요청해야 리시버에게 브로드캐스트를 보낼 수 있다.
주의할 사항
* 외부 앱에 브로드캐스트를 전송할 필요가 없다면 LocalBroadcastManager를 사용하여
자신의 앱 내에서만 브로드캐스트를 전송/수신할 수 있도록 하자. 성능/보안 측면에서 효율적이다.
* 여러 앱이 동일한 브로드캐스트를 수신하도록 정적 리시버를 등록했다면, 시스템이 많은 앱을 실행하게 된다.
결국 기기 성능에 영향을 줄 수 있기 때문에, 정적 리시버보다는 동적 리시버를 사용하자.
* 다른 앱이 악성 브로드캐스트를 자신의 앱 리시버에 전송할 수 있다.
정적 리시버라면 android:exported 속성을 false로 설정하여 다른 앱의 브로드캐스트를 수신하지 않도록 하자.
또한 동적 리시버라면 LocalBroadcastManager를 사용하자. 리시버를 등록할 때 권한을 지정하는 방법도 있다.
* 브로드캐스트의 이름은 전역이기 때문에, 이름이 고유해야 한다. 다른 앱과 브로드캐스트가 충돌할 수 있기 때문이다.
* 리시버의 onReceive() 메소드는 기본 스레드(UI 스레드)에서 실행된다.
따라서 이 메소드에서는 짧은 작업만 수행해야 한다.
긴 작업을 수행하려면 https://developer.android.com/guide/components/broadcasts?hl=ko#effects-process-state 를 참고하자.
참조
https://developer.android.com/guide/components/broadcasts?hl=ko
'app > android' 카테고리의 다른 글
[안드로이드/코틀린] registerForActivityResult() - 권한 요청하기 (0) | 2023.09.26 |
---|---|
[안드로이드/android] Bundle의 개념과 Intent의 차이점, savedInstanceState (0) | 2023.09.25 |
[안드로이드/코틀린] Intent와 Intent-filter 알아보기 (0) | 2023.09.25 |
[안드로이드/코틀린] PendingIntent 개념 알아보기 (0) | 2023.09.25 |
[안드로이드/코틀린] fragment에서 context, activity를 얻는 방법 (0) | 2023.09.23 |