Android(kotlin) - Broadcast Receiver

하동혁 ·2023년 4월 2일
0

Android

목록 보기
2/4
post-thumbnail

Broadcast Receiver

▪️ Broadcast Receiver란?

안드로이드에서 Broadcast Receiver는 앱 구성 요소 중 하나로, 다른 앱이나 시스템에서 발생한 브로드캐스트 메시지를 수신하는 역할을 합니다.

브로드캐스트 메시지는 시스템에서 발생하는 다양한 이벤트를 의미하며, 예를 들어 배터리 부족, 네트워크 상태 변경, 앱 설치/제거 등의 이벤트가 포함됩니다. 이러한 이벤트는 시스템이나 다른 앱에서 발생할 수 있으며, Broadcast Receiver는 이러한 이벤트를 수신하여 적절한 처리를 할 수 있습니다.

Broadcast Receiver는 안드로이드의 시스템 브로드캐스트를 수신하는 것 외에도, 개발자가 정의한 앱에서 발생시키는 브로드캐스트 메시지도 수신할 수 있습니다. 이를 통해 앱 내에서 이벤트를 처리하거나, 다른 앱에 메시지를 전달하는 등의 다양한 작업을 수행할 수 있습니다.


▪️ Broadcast Receiver

  • 시스템이나 다른 앱에서 보내는 브로드케스트 메시지를 받아서 처리합니다.
  • 디자인 패턴 중 publish-subscribe 형태 처럼 한쪽에서는 이벤트를 제공하기만 하고 한쪽에서는 이벤트를 받기만 합니다.
  • 앱들끼리도 미리 사전에 정의한 Action을 주고 받을 수 있습니다.

▪️ 등록 방식에 따른 종류

정적 리시버

  • AndroidManifest.xml 파일에 등록되며 라이프 사이클과 무관하게 동작합니다.
  • 앱이 설치되면 즉시 사용 가능하며 등록과 해지가 자유롭지 못합니다.

동적 리시버

  • Activity와 같은 컴포넌트에서 프로그래밍적으로 등록하며 라이프사이클 내에서 등록 및 삭제 처리가 필요합니다.
  • 해당 코드가 실행될 때 사용 가능하며 코드 내에서 필요에 따라 등록 및 삭제가 가능합니다.

Android 4대 컴포넌트 중 Broadcast Recevier만 동적으로 등록할 수 있습니다.



▪️ 정적 Receiver 구현

시스템의 locale 정보 변경시 발생되는 broadcast 수신

manifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">

    <application
        ...>
        <receiver
            android:name=".LocaleChangeReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.LOCALE_CHANGED" />
            </intent-filter>
        </receiver>
        ...
    </application>
</manifest>
  • 수신 받을 인텐트에 대한 정보를 intent-filter 내부에 선언합니다.
  • android:name: Receiver 클래스의 이름을 지정합니다. 패키지 이름을 포함한 전체 클래스 이름을 지정해야 합니다.
  • android:enabled: Receiver가 활성화되어 있는지 여부를 지정합니다. 이 값이 false로 설정되면 Receiver는 작동하지 않습니다. 기본값은 true입니다.
  • android:exported: 다른 앱에서 이 Receiver를 사용할 수 있는지 여부를 지정합니다. 이 값을 true로 설정하면 다른 앱에서도 이 Receiver를 사용할 수 있습니다. 기본값은 false입니다.
  • intent-filter: Receiver가 수신할 브로드캐스트 메시지를 지정합니다. action, category, data 등의 요소를 포함할 수 있습니다. 예제에서는 BOOT_COMPLETED와 AIRPLANE_MODE 두 가지 action을 지정하고 있습니다.

LocaleChangedRecevier.kt

class LocaleChangeReceiver : BroadcastReceiver() {
    // broadcast에서 보낸 intent
    override fun onReceive(context: Context, intent: Intent) {
        Log.d("LocaleChangedRecevier", "onReceive: receive 받기: ${intent.action}")

    }
}
  • BroadcastReceiver()를 상속 받습니다.

locale 변경하기

Settings -> System > Languages & input -> Languages

실행 결과



▪️ 동적 Receiver 구현

화면 on/off를 System Broadcast로 수신

  • 시스템은 화면의 상태에 따라 해당 인텐트를 Broadcast 합니다.
    • ACTION_SCREEN_ON(켜짐)
    • ACTION_SCREEN_OFF(꺼짐)
  • 화면이 켜지거나 꺼지는 것을 받아서 작업을 해야 할 경우 대응할 수 있습니다.
  • 동적 리시버 등록
    • 암시적 브로드캐스트 제한 사항으로 인해 위의 on/off 인텐트를 수신하려면 동적 리시버를 등록 해야 합니다.
    • registerReceiver / unregisterReceiver 메서드를 통해서 동적으로 등록/해제 합니다.

(참고) 암시적 브로드캐스트 (implicit broadcast)

안드로이드에서 암시적 브로드캐스트(implicit broadcast)는 BroadcastReceiver를 사용하여 시스템에서 발생하는 특정 이벤트를 수신하는 것을 의미합니다. 암시적 브로드캐스트는 명시적(explicit) 브로드캐스트와는 달리 특정한 액션(action)을 지정하지 않고, 그냥 인텐트(intent) 객체를 사용하여 전체 시스템에 브로드캐스트됩니다.

하지만, 안드로이드 시스템에서 모든 암시적 브로드캐스트를 수신하는 것은 안전하지 않을 수 있습니다. 왜냐하면 다른 애플리케이션에서 브로드캐스트를 발생시키면 해당 앱에서 수신할 수 있기 때문입니다. 따라서 Android 8.0(Oreo)부터는 암시적 브로드캐스트의 사용을 제한하고, 보안상 이유로 일부 브로드캐스트는 명시적으로 전달해야 합니다.

따라서, 안드로이드 앱을 개발할 때, 가능한한 명시적 브로드캐스트를 사용하도록 권장됩니다. 또한, 안드로이드 8.0 이상에서는 암시적 브로드캐스트 예외를 방지하기 위해 명시적으로 수신자(receiver)를 등록해야 합니다.


BroadcastActivity.kt

class BroadcastActivity : AppCompatActivity() {

    lateinit var receiver:BroadcastReceiver

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_broadcast)
        setTitle("BroadcastActivity")

        /***** Screen On/Off *******/
        val intentFilter2 = IntentFilter(Intent.ACTION_SCREEN_OFF)
        intentFilter2.addAction(Intent.ACTION_SCREEN_ON)
        receiver = object: BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                val action = intent.action
                Log.d(TAG, "receive : $action")

                when (action) {
                    Intent.ACTION_SCREEN_ON -> {
                        // do something
                        Log.d(TAG,"ACTION_SCREEN_ON");
                    }
                    Intent.ACTION_SCREEN_OFF -> {
                        // do something
                        Log.d(TAG,"ACTION_SCREEN_OFF");
                    }
                }
            }
        }

        // 시스템에게 intentFilter2에 해당하는 이벤트가 오면 receiver를 실행시켜달라고 등록
        registerReceiver(receiver, intentFilter2);
        /***** Screen On/Off *******/



    }


    override fun onDestroy() {
        super.onDestroy()

        // 리시버 등록을 해제
        unregisterReceiver(receiver)
    }
}
  • registerRecevier( )를 등록
  • unresgisterReceiver( )를 호출하여 해제



▪️ 사용자 정의 Receiver

manifest.xml

        <receiver android:name=".CustomReceiver" android:enabled="true"  android:exported="false" >
            <intent-filter>
                <!-- 사용자 정의 broadcast 수신 -->
                <action android:name="custom.MY" />
            </intent-filter>
        </receiver>
  • intent filter에 수신할 action 내용을 작성합니다.

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private lateinit var broadcastBtn: Button
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        broadcastBtn = findViewById(R.id.broadcast_btn)
        broadcastBtn.setOnClickListener {
            val intent = Intent(this, CustomReceiver::class.java)
            intent.action = "custom.MY"
            intent.putExtra("content", "custom recevier 테스요~~.")

            sendBroadcast(intent)
            Log.d("CustomReceiver", "onCreate: 발송 완료")
        }
    }
}
  • 명시적으로 수신할 Receiver를 지정해야 합니다.
    => val intent = Intent(this, CustomReceiver::class.java)
  • sendBroadcast(intent)를 통해 시스템에게 브로드캐스트 요청을 합니다. 이때 등록한 intent가 전달됩니다.

CustomReceiver.kt

class CustomReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d("CustomReceiver", "onReceive: receive 받기: ${intent.action}")
        Log.d("CustomReceiver", "onReceive: extra: ${intent.getStringExtra("content")}")
    }
}

0개의 댓글