Broadcast Receiver

임찬형·2022년 8월 17일
0

Android 기본

목록 보기
6/7

Broadcast Receiver 란?

시스템이 방송한 정보를 수신하고 해당 상황에 대한 동작을 정의할 수 있는 컴포넌트.

예를 들면?

전화가 걸려옴에 의한 알림 - 전화 앱 실행 동작과 연결

배터리 부족에 의한 알림 - 실행 중인 앱들 정보 저장 동작과 연결

특징

  1. 시스템 뿐만 아니라 여러 앱들이 방송을 보낼 수 있다.

  2. 앱들은 관심 있는 방송에 대해 Receiver를 정의하여 방송들을 선택해서 수신할 수 있다.

전송 방법

val intent = Intent().apply {
    action = "com.ich.test.broadcasting.action.FILE_DOWNLOAD"
    putExtra("FILE_NAME","test_file")
}
sendBroadcast(intent)

Intent에 action과 extra 등 정보를 추가한 후 sendBroadcast 함수를 이용하여 전송.

보내는 쪽에서 정의한 action을 받을 앱의 receiver에 intent-filter로 추가하면 해당 방송을 수신할 수 있다

시스템 방송에 대한 플래그(배터리 or 화면 켜짐 등 다양)도 기본적으로 존재하므로 해당 플래그로 등록할 경우 시스템 방송을 수신할 수 있다.

+) 시스템 방송들 중 일부는 부하를 막기 위해 동적 리시버만 수신하도록 한 것들도 존재한다.

Receiver의 종류

  1. 정적 리시버
    Manifest에 추가하는 방식으로, 한 번 등록하면 해제할 수 없다.
class TestReceiver: BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val fileName = intent.getStringExtra("FILE_NAME")

        Toast.makeText(context, "name: $fileName", Toast.LENGTH_SHORT).show()
    }
}

/// Manifest.xml
<receiver android:name=".TestReceiver">
	<intent-filter>
    	<action android:name="com.ich.test.broadcasting.action.FILE_DOWNLOAD"
	</intent-filter>
</receiver>

위처럼 BroadcastReceiver를 상속받은 클래스를 생성하고, 방송에 대한 동작을 위한 onReceive 함수를 재정의한다.

이후 Manifest에서 컴포넌트 레벨(액티비티와 동일한 레벨)에 리시버를 정의하고 생성한 Receiver 클래스를 넣는다.

추가로, 정적 리시버는 동시에 처리되지 않고 순차적으로 처리된다.

따라서 한 리시버의 처리가 늦어지면 다음 리시버들의 처리도 늦어진다.

  1. 동적 리시버
    Manifest에 추가하지 않고 다른 컴포넌트 내에서 동적으로 등록, 해제한다.
val intentFilter = IntentFilter()
intentFilter.addAction("com.ich.test.broadcasting.action.FILE_DOWNLOAD")

val receiver = object: BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val fileName = intent.getStringExtra("FILE_NAME")

        Toast.makeText(context, "name: $fileName", Toast.LENGTH_SHORT).show()            
    }
}

registerReceiver(receiver, intentFilter)

위처럼 Maniest 정의 없이 IntentFilter와 BroadcastReceiver 객체를 생성하고, registerReceiver 함수를 이용하여 리시버를 등록한다.

동적 리시버는 다른 컴포넌트, 주로 액티비티에서 등록하므로 컴포넌트의 생명 주기가 끝나면 동작하지 않는다.

따라서 메모리 누수를 방지하기 위해 컴포넌트의 생명주기에 맞춰 등록을 해제하는 과정이 필요하다.

액티비티의 경우 onDestroy() 생명주기 함수에서 unRegisterReceiver 함수를 호출하여 리시버를 해제한다.

추가로, 동적 리시버는 동시에 처리될 수 있다.

5개의 리시버를 1~5순서로 등록한 후 처리하는 경우, 1~5순서로 리시버가 호출된다.

하지만 동시에 처리되기 때문에 처리 완료시간은 호출 순서와 달라질 수 있다.

+) 동적 리시버를 순서대로 처리하게 하고 싶다면 sendBroadcast함수 대신 sendOrderedBroadcast함수를 사용하면 된다.

플래그

사용자가 의도하지 않게 모든 시스템 방송을 수신하여 부하를 받게 되거나, 리시버를 통해 정보를 빼가기 위한 악의적인 앱 등을 방지하기 위해 플래그가 존재한다.

아래 플래그들은 intent.addFlags 함수로 추가할 수 있다.

  1. FLAG_EXCLUDE_STOPPED_PACKAGES (기본 설정)
    한 번이라도 실행되어야 해당 앱의 정적 리시버가 동작할 수 있다.

  2. FLAG_INCLUDE_STOPPED_PACKAGES
    위 동작과 반대로, 실행되지 않은 앱의 리시버가 동작할 수 있도록 한다.

  3. FLAG_RECEIVER_REGISTERED_ONLY
    동적 리시버만 방송을 수신할 수 있도록 한다. (특정 앱 정적 리시버가 몰래 실행되는 것 방지)

  4. FLAG_RECEIVER_REPLACE_PENDING
    중복해서 동일한 액션으로 방송될 경우 중복을 제거한다.

  5. FLAG_RECEIVER_FOREGROUND
    방송을 Foreground로 설정하여 우선 처리를 요구하도록 보낸다.

동작 시간 제한

BroadcastReceiver는 메인 스레드에서 처리되는 컴포넌트이다.
따라서 ANR이 발생할 수 있다.

Foreground Receiver의 경우 10초
Background Receiver의 경우 60초

ANR 발생 시간이 존재한다.

위에서 언급하였다시피 Receiver는 메인 스레드 동작이므로 오래 걸리는 작업을 넣어 두면 액티비티에도 영향을 줄 수 있다.

오래 걸리는 작업을 해야 한다면 스레드 또는 서비스를 사용하자.

LocalBroadcast

Global Broadcast는 다음의 문제점들이 있다.

  1. 시스템 경계를 넘어 모든 Receiver들에게 전달된다.
  2. 발송한 Broadcast 정보를 다른 앱이 감시할 수 있다.
  3. 시스템 부하가 증가한다.

다른 앱에 Broadcast를 보낼 필요가 없을 경우 Broadcast를 발송시킨 앱 밖으로 내보내지 않도록 하는 것이 LocalBroadcast이다.

LocalBroadcastManager 객체를 통해 인스턴스를 얻어올 수 있으며 사용 방법은 유사하다.

// 송신
val intent = Intent().apply {
    action = "com.ich.test.broadcasting.action.LOCAL_BROADCAST"
}
val broadcastManager = LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
//수신
val intentFilter = IntentFilter()
intentFilter.addAction("com.ich.test.broadcasting.action.LOCAL_BROADCAST")

val receiver = object: BroadcastReceiver(){
    override fun onReceive(context: Context, intent: Intent) {
       // 처리
    }
}
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, intentFilter)

0개의 댓글