본문 바로가기

안드로이드

[안드로이드] 브로드캐스트 리시버

브로드캐스트 리시버(BroadcastReceiver)

브로드캐스트란 네트워크를 학습하면 알겠지만 방송을 의미한다. 여기서 중요한 특징은 방송국에서 방송을 송신하면 필요한 사람만 해당 정보를 수신하게 된다. 이러한 메커니즘을 그대로 사용하는게 안드로이드의 Broadcast Receiver이다. 그리고 이러한 방송은 방송하는 앱 자체의 리시버에게 전달되는 것 뿐만 아니라 다른 앱으로도 방송될 수 있다.

 

어딘가에서 특정한 방송을 송신하면, 특정 방송을 기다리고 있던 브로드캐스트 리시버는 이를 듣고 처리를 시작하게 된다. 

 

앱에서 이러한 브로드캐스트를 받기 위해서는 receiver를 등록해야한다. BroadcastReceiver를 상속한 객체를 생성하여 manifest에 등록하거나 registerReceiver()로 등록해야한다. 전자를 정적 후자를 동적 BroadcastReceiver라고 한다.


정적 BroadcastReceiver 등록

이는 BroadcastReceiver를 앱을 설치하는 시점에 등록하는 방식이다. 공식 가이드에서는 Manifest-declared receiver라고 표현한다.

 

특징은 다음과 같다.

  • 앱이 실행되지 않는 상황에도 리시버가 등록이 되어있기 때문에 모든 이벤트를 수신할 수 있다.
  • 하지만 리시버를 해제할 수는 없다.

구현 방법

  1. BroadcastReceiver를 상속한 클래스를 구현한다.
  2. 구현한 클래스를 AndroidManifest.xml에 선언한다.

1. BroadcastReceiver를 상속한 클래스를 구현한다.

class MyReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d("MyReceiver", "Intent: $intent")
    }
}

 

2. 구현한 클래스를 AndroidManifest.xml에 선언한다.

<receiver android:name=".MyReceiver">
    <intent-filter>
        <action android:name="android.intent.action.LOCALE_CHANGED"/>
        <action android:name="com.example.action.test"/>
    </intent-filter>
</receiver>

위에서 intent-filter에 선언된 action에 주목해야한다. LOCALE_CHANGED라는 action이나 개발자가 정의한 커스텀 action이 송신될 경우 리시버는 이를 수신하여 onReceived 메서드를 수행하게 된다.


동적 BroadcastReceiver 등록

동적 BroadcastReceiver는 앱 실행 중에 코드레벨에서 등록되는 리시버를 의미한다. 공식 가이드에서는 Context-registerd receiver라고 표현한다. Context(activity)에 등록되기 때문에 다음과 같은 이름이 부여되었다.

 

특징은 다음과 같다.

  • 원하느 시간, 상황에 리시버를 등록하고 해제할 수 있다.
  • 앱이 실행되지 않으면 리시버를 등록할 수 없다.

구현방법

  1. 리시버 객체를 생성한다.
  2. registerRecevier() 메서드로 리시버를 등록한다.
  3. unregisterReceiver() 메서드로 리시버를 헤제한다.

1. 리시버 객체를 생성한다. 혹은 리시버 클래스를 구현하고 객체를 생성한다.

//Manifest-Declared 방식처럼 먼저 클래스를 구현하고 객체를 생성하는 방식
private val br = MyBroadcastReceiver()

class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d("MyBroadcastReceiver", "Intent: $intent")
    }
}

//익명 클래스로 BroadcastReceiver를 상속받는 객체를 생성하는 방식.
private val br: BroadcastReceiver = object:BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.d("Receiver", "Intent: $intent")
    }
}

위 둘중 구현방식은 편한 방법을 선택하면된다.

 

2. registerReceiver() 메서드로 리시버를 등록한다.

private val broadcastRecevier = MyBroadcastReceiver()

override fun onCreate(savedInstanceState: Bundle?) {
    val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
    registerReceiver(broadcastRecevier, filter)
}

Manifest-Declared 방식과의 차이점은 인텐트 필터를 코드레벨에서 제공해준다.

 

3. unregisterReceiver() 메서드로 리시버를 헤제한다.

override fun onDestroy() {
    unregisterReceiver(broadcastReceiver)
}

 동적 방식은 context 기반으로 리시버를 등록하기 때문에 context가 소멸되면 리시버는 해제된다. context 기반이라는 개념에 의해 등록/해제느 다음 시점이 적절하다.

  • onCreate()에서 등록/ onDestroy()에서 해제
  • onResume()에서 등록/ onPause()에서 해제

Receiver로 브로드캐스트를 전달하기

브로드캐스트를 전달하는 방법은 두가지 방법이 존재한다.

  • sendBroadcast(Intent): 모든 리시버들에게 동시에 전달.
  • sendOrderedBroadcast(Intent, String): 리시버의 우선순위를 참고하여 순서대로 전달. 이는 첫번째 리시버가 방송을 전달받으면 해당 리시버에서 모든 작업이 끝날 경우 다음 리시버로 방송이 전달되어 수행된다. 이때, 이전 리시버에서 처리된 결과를 얻거나, 다음 리시버로 방송 전달이 안되도록 구현할 수 있다.

위처럼 순서와 연관된 것 이외에도 암시적인 전달/명시적인 전달이 존재한다.

  • 암시적인 전달: 인텐트와 관련된 모든 리시버에게 전달한다.
  • 명시적인 전달: 특정 브로드캐스트로만 방송을 전달한다.
//암시적 전달
val intent = Intent("com.example.action.test")
sendBroadcast(intent)

//명시적 전달
val intent = Intent("com.example.action.test")
intent.setPackage("com.example.broadcastreceiver")
sendBroadcast(intent)

다만, 중요한 점은 암시적인 방법으로는 Manifest-Declared 리시버는 방송을 전달받을 수 없다. 왜냐면 해당하는 인텐트를 갖고있는 모든 앱들이 실행되어 버벅임과 같은 문제가 생길 수 있다. 다만, ACTION_BOOT_COMPLETED와 같은 방송은 받을수 있다.


권한을 갖고 있는 앱의 방송만 수신

만약 개발자는 초기에 자신의 앱 내부에서 방송을 송/수신하기 위해 action을 정의했는데 어떤 악성 앱이 이 action을 알고 동일한 action을 브로드캐스트하여 리시버를 동작시킬 수 있다. 이때는 권한을 설정하여 이런 일을 방지할 수 있다.

 

<receiver android:name=".MyReceiver"
        android:permission="android.permission.SEND_SMS">
    <intent-filter>
        <action android:name="com.example.action.test"/>
    </intent-filter>
</receiver>

위 코드의 경우 com.example.action.test 인텐트를 보내는 앱에서 SEND_SMS 권한을 갖고 있지 않다면 리시버에서는 이를 수신하지 않는다.

 

권한을 갖고 있는 리시버에게만 브로드캐스트 전달

다음과 같이 sendBroadcast의 두번재 매개변수로 권한을 전달하면 간단하게 구현 가능하다.

Intent intent = new Intent("com.example.action.test");
intent.setPackage("com.example.broadcastreceiver");
sendBroadcast(intent, Manifest.permission.SEND_SMS);

참고