관리 메뉴

나만을 위한 블로그

[Android Compose] BroadcastReceiver 사용법 본문

Android/Compose

[Android Compose] BroadcastReceiver 사용법

참깨빵위에참깨빵 2023. 8. 15. 21:07
728x90
반응형

Compose로 앱을 만들 때도 당연히 브로드캐스트 리시버를 사용해야 하는 경우가 발생할 수 있다.

이 포스팅에선 기기에서 비행기 모드를 온오프 했을 때 이를 감지하는 것, 버튼을 누르면 내가 정의한 인텐트를 브로드캐스트 리시버를 통해 받는 예제와 관련 코드 분석을 진행한다.

그러나 브로드캐스트 리시버가 무엇인지 잘 모를 수 있으니 먼저 브로드캐스트 리시버가 무엇인지 확인하고 넘어간다.

 

브로드캐스트 리시버(Broadcast Receiver)는 어떻게 작동할까? 라디오를 생각해 보자. 라디오는 여러 주파수에서 방송하는 많은 채널들이 있다. 난 주파수를 바꿔가면서 원하는 채널을 들을 수 있다.

이와 유사하게 브로드캐스트 리시버는 안드로이드 시스템에서 발생하는 여러 이벤트를 듣고 반응한다. 배터리 부족, 비행기 모드 온오프 등이 그것이다. 이런 이벤트들을 듣고 반응할 수 있게 브로드캐스트 리시버를 등록하면, 나중에 안드로이드에서 알림을 보내면 앱에 등록한 브로드캐스트 리시버가 감지해서 관련 작업을 수행하는 것이다.

아래는 디벨로퍼 공식문서에서 말하는 브로드캐스트 리시버다.

 

https://developer.android.com/guide/components/broadcasts?hl=ko 

 

브로드캐스트 개요  |  Android 개발자  |  Android Developers

브로드캐스트 개요 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Android 앱은 Android 시스템 및 기타 Android 앱에서 게시-구독 디자인 패턴과 유사한 브로드캐

developer.android.com

앱은 안드로이드 시스템 및 기타 앱에서 게시/구독 디자인 패턴과 유사한 브로드캐스트 메시지를 받거나 보낼 수 있다. 관심있는 이벤트가 발생할 때 이런 브로드캐스트가 전송된다. 예를 들어 안드로이드 시스템은 시스템 부팅 또는 기기 충전 시작 같은 여러 시스템 이벤트가 발생할 때 브로드캐스트를 전송한다. 또한 앱은 맞춤 브로드캐스트를 전송해서 다른 앱이 관심 가질만한 사항(일부 새 데이터가 다운로드됨)을 관련 앱에 알릴 수 있다
앱은 특정 브로드캐스트를 받도록 등록할 수 있다. 브로드캐스트가 전송되면 시스템은 특정 유형의 브로드캐스트를 받도록 신청한 앱에 브로드캐스트를 자동으로 라우팅한다
브로드캐스트는 앱 전체에 걸쳐서, 일반 사용자 플로우 외부에서 메시징 시스템으로 쓰일 수 있다. 그러나 브로드캐스트에 응답하고 백그라운드에서 시스템 성능 저하의 원인이 될 수 있는 작업 실행을 남용하지 않도록 주의해야 한다

시스템이 비행기 모드로 전환하고 해제할 때 같이 여러 시스템 이벤트가 발생하면 시스템은 자동으로 브로드캐스트를 전송한다. 시스템 브로드캐스트는 이벤트를 수신하도록 신청한 모든 앱에 전송된다. 브로드캐스트 메시지 자체는 작업 문자열이 발생된 이벤트를 식별하는 인텐트 객체에서 래핑된다. 또한 인텐트에는 번들로 제공되는 추가 정보가 포함될 수 있다...(중략)

 

디벨로퍼에서 설명하는 대로, 브로드캐스트 리시버는 안드로이드 시스템 레벨에서, 또는 내 핸드폰에 설치된 다른 앱에서 보내는 메시지를 받을 수 있는 컴포넌트다.

더 자세한 내용은 알아서 확인하고 이제 코드를 본다.

 

먼저 비행기 모드의 온오프 여부를 확인하는 브로드캐스트 리시버를 상속한 클래스다.

 

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.provider.Settings

class AirPlaneModeReceiver: BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        if (intent?.action == Intent.ACTION_AIRPLANE_MODE_CHANGED) {
            val isTurnedOn: Boolean = Settings.Global.getInt(
                context?.contentResolver,
                Settings.Global.AIRPLANE_MODE_ON
            ) != 0
            println("## 비행기 모드 켜짐? $isTurnedOn")
        }
    }
}

 

isTurnedOn에 비행기 모드 온오프 여부가 Boolean 값으로 담긴다. 그리고 Boolean 값이 정상적으로 담겨졌는지 확인하기 위해 println을 써서 확인한다.

다음은 인텐트를 통해 특정 액션을 받았을 경우 println()을 호출하는 작업을 하는 브로드캐스트 리시버다.

 

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent

class TestReceiver: BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        if (intent?.action == "테스트 액션") {
            println("## 테스트 인텐트 받음!")
        }
    }
}

 

그리고 액티비티에서 이 리시버들을 사용한다.

 

import android.annotation.SuppressLint
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.composeprac.broadcast_receiver.ui.theme.ComposePracTheme

class BroadcastTestActivity : ComponentActivity() {

    private val airPlaneModeReceiver = AirPlaneModeReceiver()
    private val testReceiver = TestReceiver()

    @SuppressLint("UnspecifiedRegisterReceiverFlag")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        registerReceiver(
            airPlaneModeReceiver,
            IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
        )
        registerReceiver(
            testReceiver,
            IntentFilter("테스트 액션")
        )
        setContent {
            ComposePracTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        Button(onClick = {
                            sendBroadcast(
                                Intent("테스트 액션")
                            )
                        }) {
                            Text(text = "브로드캐스트 전송")
                        }
                    }
                }
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(airPlaneModeReceiver)
        unregisterReceiver(testReceiver)
    }
}

 

이게 끝이다. 이후 앱을 실행하면 버튼 하나만 놓여져 있다.

 

 

버튼을 누르면 sendBroadcast()를 통해 인텐트에 "테스트 액션" 문자열을 넣어서 해당 인텐트를 수신하는 브로드캐스트 리시버에 전송한다. 또는 비행기 모드를 켜고 키면 콘솔에 true, false가 출력된다.

 

 

이제 액티비티 코드를 확인해 본다.

 

registerReceiver(
    airPlaneModeReceiver,
    IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)
)
registerReceiver(
    testReceiver,
    IntentFilter("테스트 액션")
)

 

먼저 브로드캐스트 리시버는 정적, 동적으로 나눠진다. 정적 브로드캐스트 리시버는 앱이 종료되거나 핸드폰을 잠금 모드로 바꿔도 브로드캐스트를 받아서 로직이 실행되고 동적 브로드캐스트 리시버는 앱 실행 중에만 브로드캐스트를 받아 로직이 실행된다.

정적 리시버를 사용하는 편이 더 나아 보이겠지만 안드로이드 오레오부터는 몇 개를 제외하고 암시적 브로드캐스트 리시버를 매니페스트에 등록할 수 없도록 바뀌었다. 아래는 API 레벨에 상관없이 등록해서 사용할 수 있는 브로드캐스트 리시버 종류를 정리한 문서다.

 

https://developer.android.com/guide/components/broadcast-exceptions?hl=ko 

 

암시적 브로드캐스트 예외  |  Android 개발자  |  Android Developers

백그라운드 제한에서 제외되는 암시적 브로드캐스트입니다.

developer.android.com

 

본론으로 돌아와서 registerReceiver()는 동적으로 브로드캐스트 리시버를 등록하기 위한 함수다. 이를 써서 앞서 만든 2가지 브로드캐스트 리시버를 등록하고 인텐트 필터도 같이 설정해서, 특정 암시적 인텐트를 받으면 그에 맞게 브로드캐스트 리시버가 호출되게 한다.

이렇게 onCreate에서 등록한 브로드캐스트 리시버들은 onDestroy()에서 unregisterReceiver()를 호출해 액티비티가 완전 파괴되면 같이 없애준다.

 

override fun onDestroy() {
    super.onDestroy()
    unregisterReceiver(airPlaneModeReceiver)
    unregisterReceiver(testReceiver)
}

 

 

이렇게 Compose에서 정적으로 브로드캐스트 리시버를 등록할 수 있다. 버튼을 만들고 클릭하는 코드가 Compose로 바뀐 것이고 나머지 코드는 Compose 이전과 동일하기 때문에 별 문제 없이 이해할 수 있을 것이다.

 

반응형
Comments