240127 TIL #306 Android #19 브로드캐스트 리시버

김춘복·2024년 1월 27일
0

TIL : Today I Learned

목록 보기
306/543
post-custom-banner

Today I Learned

오늘은 브로드캐스트 리시버에 대해 공부했다.


브로드캐스트 리시버

이벤트 모델로 실행되는 컴포넌트. 줄여서 리시버

  • 이벤트 : 사용자이벤트 x. 시스템의 특정한 상황

  • 리시버도 안드로이드의 컴포넌트이므로 인텐트를 시스템에 전달함으로써 실행한다.


브로드캐스트 리시버 생성

class MyReceiver: BroadcastReceiver(){
    override fun onReceive(context: Context?, intent: Intent?) {
        TODO("Not yet implemented")
    }
}
  • 리시버는 BroadcastReceiver를 상속받는 클래스를 선언해야 한다.

  • 생명주기 함수는 onReceiver() 하나 뿐이다. 어디에서 리시버를 실행시키려고 인텐틀르 시작하면 이 함수가 자동으로 호출된다. 그리고 자신을 호출한 intent 객체를 매개변수로 받는다.

  • onReceiver()함수는 실행 후 10초내에 완료할것을 권장하므로 오래걸리는 작업은 부적절하다.

  • onReceiver()함수가 실행을 마치면 리시버 객체는 소멸한다.

  • 특정한 상황에 리시버를 항상 실행해야한다면, 매니페스트 파일에 등록해 사용한다.
    태그는 <receiver>, 필수 속성은 name. 암시적 인텐트로 실행하려면 <intent-filter>를 선언해야한다.

        <receiver android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
        </receiver>

동적 등록과 해제

리시버는 매니페스트에 등록하지 않고 코드에서 필요한 순간에 동적으로 등록할 수도 있다. 특정 액티비티나 서비스가 동작할 때만 브로드캐스트 리시버를 실행해야한다면 동적으로 등록하는 방법을 사용한다.

// 리시버 객체 생성
val receiver = object: BroadcastReceiver(){
    override fun onReceive(context: Context?, intent: Intent?) {
        TODO("Not yet implemented")
    }
}
// 동적 등록
val filter = IntentFilter("ACTION_RECEIVER")
registerReceiver(receiver,filter)
// 등록 해제
unregisterReceiver(receiver)
  • 필요한 순간에 registerReceiver() 함수를 이용해 시스템에 등록한다.

  • 사용 후, 필요없으면 unregisterReceiver() 함수를 이용해 해제해줘야 한다.


브로드캐스트 리시버 실행

  • 리시버를 실행하려면 인텐트가 필요하다. 클래스명만 등록했으면 명시적 인텐트로, 인텐트 필터를 등록했으면 암시적 인텐트로 실행한다.

  • 리시버를 매니페스트 파일에 등록하고 <intent-filter> 태그를 선언했다면 암시적 인텐트로는 실행할 수 없다.

  • 코드에서 registerReceiver() 함수로 등록한 리시버는 암시적 인텐트로도 잘 실행된다.

  • 리시버를 실행하는 인텐트는 sendBroadcast()함수로 시스템에 전달한다.

val intent = Intent(this, MyReceiver::class.java)
sendBroadcast(intent)
  • 액티비티 인텐트와 리시버 인텐트 비교

시스템 상태 파악

시스템에서 발생하는 인텐트는 여러 종류가 있다. 부팅완료, 화면on/off, 배터리 상태 등이 대표적이다.

부팅 완료

  • 부팅 완료시 특정 작업을 앱에서 수행하고 싶으면 리시버를 만들고 매니페스트 파일에 인텐트 필터를 구성해 등록한다.

  • 부팅이 완료되면 시스템에서는 android.intent.action.BOOT_COMPLETED라는 액션 문자열을 포함하는 인텐트가 발생한다.

  • 리시버와 인텐트 필터 등록
    매니페스트 상위에 퍼미션을 추가해야한다.

<uses-permission android:name="android.intent.action.BOOT_COMPLETED"/>
...
<receiver android:name=".MyReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
</receiver>

화면 on/off

  • 화면을 켜거나 끄는 상황을 감지하는 브로드캐스트 리시버는 매니페스트에 등록하면 실행되지 않는다.

  • 따라서, 액티비티나 서비스 컴포넌트의 코드에서 registerReceiver() 함수를 이용해 동적으로 등록해야 한다.

  • android.intent.action.SCREEN_ONandroid.intent.action.SCREEN_OFF 액션 문자열을 상수 변수로 사용한다.

        val receiver = object: BroadcastReceiver(){
            override fun onReceive(context: Context?, intent: Intent?) {
                when(intent?.action){
                    Intent.ACTION_SCREEN_ON -> Log.d("event", "화면 켜짐")
                    Intent.ACTION_SCREEN_OFF -> Log.d("event", "화면 꺼짐")
                }
            }
        }
        // 리시버 등록
        val filter = IntentFilter(Intent.ACTION_SCREEN_ON).apply {
            addAction(Intent.ACTION_SCREEN_OFF)
        }
        registerReceiver(receiver,filter)
        // 리시버 등록 해제
        unregisterReceiver(receiver)

배터리 상태

현재 기기에 전원이 얼마나 공급되는지, 충전량은 얼마나 되는지 등을 나타낸다.

  • 안드로이드 시스템에서 배터리 상태가 변경되면 다음 액션 문자열로 인텐트가 발생한다.
    BATTERY_LOW: 배터리가 낮은 상태로 변경되는 순간
    BATTERY_OKAY: 배터리가 정상 상태로 변경되는 순간
    BATTERY_CHANGED: 충전 상태가 변경되는 순간
    ACTION_POWER_CONNECTED: 전원이 공급되기 시작한 순간
    ACTION_POWER_DISCONNECTED: 전원 공급을 끊은 순간

  • 리시버 작성(시스템에서 상태가 변경(이벤트 발생)되었을때)

val receiver = object: BroadcastReceiver(){
    override fun onReceive(context: Context?, intent: Intent?) {
        when(intent?.action){
            Intent.ACTION_BATTERY_OKAY -> Log.d("event", "ACTION_BATTERY_OKAY")
            Intent.ACTION_BATTERY_LOW -> Log.d("event", "ACTION_BATTERY_LOW")
            Intent.ACTION_BATTERY_CHANGED -> Log.d("event", "ACTION_BATTERY_CHANGED")
            Intent.ACTION_POWER_CONNECTED -> Log.d("event", "ACTION_POWER_CONNECTED")
            Intent.ACTION_POWER_DISCONNECTED -> Log.d("event", "ACTION_POWER_DISCONNECTED")
        }
    }
}
// 리시버 등록
val filter = IntentFilter(Intent.ACTION_BATTERY_OKAY).apply {
    addAction(Intent.ACTION_BATTERY_LOW)
    addAction(Intent.ACTION_BATTERY_CHANGED)
    addAction(Intent.ACTION_POWER_CONNECTED)
    addAction(Intent.ACTION_POWER_DISCONNECTED)
}
registerReceiver(receiver,filter)
// 리시버 등록 해제
unregisterReceiver(receiver)
  • 하지만 위와 달리 시스템 상태가 변경되지 않고도(배터리 관련 인텐트를 발생시키지 않고도), 현재 배터리 상태가 필요한 경우가 있다.
    이때는 첫 매개변수를 null로해서 registerReceiver()함수를 실행한다.
        val intentFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
        val batteryStatus = registerReceiver(null, intentFilter)
  • 현재 전원이 공급되는지 확인
val status = batteryStatus!!.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
if (status==BatteryManager.BATTERY_STATUS_CHARGING){ //전원 공급상태
    val chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
    when(chargePlug){
        // 저속충전
        BatteryManager.BATTERY_PLUGGED_USB -> Log.d("event", "usb charge")
        // 고속충전
        BatteryManager.BATTERY_PLUGGED_AC -> Log.d("event", "ac charge")
    }
}else{ // 전원 미공급상태
    Log.d("event", "not charging")
}
  • 배터리가 얼마나 충전되었는지 확인
val level = batteryStatus!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale = batteryStatus!!.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
val batteryPct = level/scale.toFloat() * 100
Log.d("event", "batteryPct : $batteryPct")
profile
Backend Dev / Data Engineer
post-custom-banner

0개의 댓글