본문 바로가기
깡샘 코틀린

14-2 시스템 상태 파악하기

by 농농씨 2023. 7. 3.

시스템에서 발생하는 인텐트는 여러 종류가 있으며 부팅 완료, 화면 켬/끔, 배터리 상태 등이 대표적이다. 이러한 상황에서 앱의 리시버를 어떻게 실행하는지 알아보자.

 

 

부팅 완료

만약 전원을 누르고 앱에서 부팅이 완료될 때 특정한 작업을 수행(목적)하고 싶다면 1️⃣브로드캐스트 리시버를 만들고 2️⃣매니페스트 파일에 인텐트 필터를 구성해서 등록한다.

부팅이 완료되면 시스템에서는 android.intent.action.BOOT_COMPLETED라는 액션 문자열을 포함하는 인텐트가 발생한다. 이때 실행하고 싶은 리시버에는 <action>의 name 속성에 똑같은 액션 문자열을 똑같이 등록하면 된다.

// 브로드캐스트 리시버와 인텐트 필터 등록
<receiver // 리시버 태그에
    android:name=".MyReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter> // 인텐트 필터 등록
    	<action android:name="android.intent.action.BOOT_COMPLETED" />
        // 부팅 완료될 때 발생하는 인텐트의 액션문자열을 브로드캐스트 리시버의 인텐트 필터의 name 속성에 똑같이 넣어주기
    </intent-filter>
</receiver>

그런데 리시버를 실행하려면 권한이 필요하므로 매니페스트에 다음처럼 퍼미션을 추가해야 한다.

// 권한 설정
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

 

 

화면 켬/끔

다음으로는 안드로이드 기기의 화면을 켜거나 끌 때 이를 감지하는 리시버를 알아보자. 그런데 화면을 켜거나 끄는 상황을 감지하는 브로드캐스트 리시버는 매니페스트에 등록하면 실행되지 않는다. 액티비티나 서비스 컴포넌트의 코드에서 registerReceiver() 함수를 이용해 동적으로 등록해야만 하는 예 중에 하나이다.

화면을 켤 때와 끌 때를구분해서 브로드캐스트 리시버를 따로 만들어도 되지만 다음처럼 하나의 리시버에서 켜고 끄는 상황을 모두 감지하도록 할 수도 있다. 다음 코드에서는 android.intent.action.SCREEN_ON과 android.intent.action.SCREEN_OFF 액션 문자열을 상수 변수로 사용했다.(what is 상수변수? 코드 상단에 지정하는거 말하는건가)

// 화면 켬/끔 리시버
receiver = object : BroadcastReceiver() { // 코드에서 동적으로 리시버 등록함
    override fun onReceive(context: Context?, intent: Intent?) {
    // 브로드캐스트 리시버의 유일한 생명주기 함수인 onReceive()
    // 항상 리시버 실행 위한 인텐트가 시작될 때 함께 호출되고 실행역할 다하면 객체 사라짐
    // 자신을 호출한 인텐트 객체를 매개변수로 받음
    	when (intent?.action) {
            Intent.ACTION_SCREEN_ON -> Log.d("kkang", "screen on") // 화면 켤 때
            Intent.ACTION_SCREEN_OFF -> Log.d("kkang", "screen off") // 화면 끌 때
        }
    }
}

이렇게 하면 onReceive() 함수의 매개변수인 Intent 객체에서 자신을 호출한 인텐트의 액션 문자열을 가져와 화면을 켜거나 끄는 상황을 판단할 수 있다.

이렇게 정의한 브로드캐스트 리시버 객체는 액션 문자열 정보로 구성한 인텐트 필터와 함께 registerReceier() 함수로 시스템에 등록한다.

// 리시버 등록
val filter = IntentFilter(Intent.ACTION_SCREEN_ON).apply { // 액션 문자열 정보로 필터 구성
	addAction(Intent.ACTION_SCREEN_OFF)
}
registerReceiver(receiver, filter) // 함수로 시스템에 브로드캐스트 객체 등록함

registerReceiver() 함수로 등록한 브로드캐스트 리시버는 필요 없는 순간이 되면 unregisterReceiver() 함수로 등록을 해제해 줘야 한다.

// 리시버 등록 해제
unregisterReceiver(receiver)

 

배터리 상태

배터리 상태는 현재 기기에 전원이 공급되는지, 충전량은 얼마나 되는지 등을 나타낸다. 이처럼 배터리와 관련된 정보나 상태 변화를 앱에서 감지할 수 있다. 안드로이드 시스템에서 배터리 상태가 변경되면 다음 액션 문자열로 인텐트가 발생한다.

  • BATTERY_LOW: 배터리가 낮은 상태로 변경되는 순간
  • BATTERY_OKAY: 배터리가 정상 상태로 변경되는 순간
  • BATTERY_CHANGED: 충전 상태가 변경되는 순간
  • ACTION_POWER_CONNECTED: 전원이 공급되기 시작한 순간
  • ACTION_POWER_DISCONNECTED: 전원 공급을 끊은 순간

이러한 배터리 관련 이벤트가 발생할 때 실행될 브로드캐스트 리시버는 다음처럼 작성할 수 있다.

// 배터리 상태 리시버
receiver = object : BroadcastReceiver() { // 리시버 동적 등록
    override fun onReceive(context: Context?, intent: Intent?) { // 자신을 호출한 인텐트 객체를 매개변수로 받음
    	when (intent?.action) { // 각 상황 발생할 때 인텐트 발생시킨다는 뜻?
            Intent.BATTERY_OKAY -> Log.d("kkang", "ACTION_BATTERY_OKAY") // 배터리 정상상태로 변경될 때
            Intent.BATTERY_LOW -> Log.d("kkang", "ACTION_BATTERY_LOW") // 배터리 낮은 상태로 변경
            Intent.BATTERY_CHANGED -> Log.d("kkang", "ACTION_BATTERY_CHANGED") // 충전상태 변경될 때
            Intent.ACTION_POWER_CONNECTED -> Log.d("kkang", "ACTION_POWER_CONNECTED") // 전원 공급 시작 순간
            Intent.ACTION_POWER_DISCONNECTED -> Log.d("kkang", "ACTION_POWER_DISCONNECTED") // 전원 공급 끊길 때

        }
    }
}

이렇게 만든 브로드캐스트 리시버와 함께 액션 문자열을 다음처럼 시스템에 등록한다.

// 리시버 등록
val filter = IntentFilter(Intent.ACTION_BATTERY_LOW).apply { // 필터 구성
    addAction(Intent.BATTERY_OKAY)
    addAction(Intent.BATTERY_CHANGED)
    addAction(Intent.ACTION_POWER_CONNECTED)
    addAction(Intent.ACTION_POWER_DISCONNECTED)
}
registerReceiver(receiver, filter) // 시스템에 리시버 등록

이처럼 브로드캐스트리시버는 시스템에서 상태가 변경되었을 때 인텐트를 발생시켜 줘야만 실행된다.

 

그런데 시스템이 배터리 관련 인텐트를 발생시키지 않아도 현재 배터리 상태가 필요할 때도 있다. 예를 들어 어떤 액티비티가 실행될 때 그 시점에 사용자의 기기에 전원이 공급되는지를 알고 싶은 경우이다. 즉, 시스템의 상태가 변경되지 않았지만 배터리 정보가 필요한 상황이다. 이럴 때는 다음처럼 작성할 수 있다.

// 시스템 인텐트 없이 배터리 상태 파악하기
val intentFilter = IntentFiilter(Intent.ACTION_BATTERY_CHANGED) // 문자열 지정해서 IntentFilter 객체 만들기
val batteryStatus = registerReceiver(null, intentFilter) // 이 정보로 리시버 객체를 시스템에 등록

배터리 상태를 알려면 1️⃣액션 문자열을 Intent.ACTION_BATTERY_CHANGED로 지정한 IntentFilter 객체를 만들고, 이 IntentFilter 정보를 이용해서 2️⃣registerReceiver() 함수로 브로드캐스트 리시버를 등록해야 한다. 이때 첫번째 매개변수를 null로 지정한다. 결국 특정 브로드캐스트 리시버를 등록하는 코드가 아니라 배터리와 관련된 정보를 registerReceiver() 함수의 반환값인 인텐트 객체의 엑스트라 정보로 등록하는 코드이다.

// 인텐트의 엑스트라를 이용해 배터리 상태 파악하기
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("kkang", "usb charge") // 저속 충전
        BatteryManager.BATTERY_PLUGGED_AC -> Log.d("kkang", "ac charge") // 고속 충전
    }
} else {
    // 전원이 공급되지 않고 있다면
    Log.d("kkang", "not charging")
}

 

현재 전원이 공급되는지는 Battery.Manager.EXTRA_STATUS로 얻는 엑스트라값으로 알아낼 수 있다. 이 값이 BatteryManager.BATTERY_STATUS_CHARGING이면 전원이 공급되는 상태이다. 또한 전원이 공급된다면 BatteryManager.EXTRA_PLUGGED로 엑스트라값을 얻을 수 있으며, 이때 엑스트라값이 BatteryManager.BATTERY_PLUGGED_USB이면 저속 충전 상태를, BatteryManager.BATTERY_PLUGGED_AC이면 고속 충전 상태를 의미한다.

 

앞에서는 현재 사용자 기기에 전원이 공급되는지를 알아봤는데 때로는 배터리가 얼마나 충전되었는지 알고 싶은 경우도 있다. 이때는 다음처럼 작성한다.

// 배터리 충전량을 퍼센트로 출력
val level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
val batteryPct = level / scale.toFloat() * 100
Log.d("kkang", "batteryPct : $batteryPct")

 

'깡샘 코틀린' 카테고리의 다른 글

15-2 바인딩 서비스  (0) 2023.07.07
15-1 서비스 컴포넌트  (0) 2023.07.06
14-1 브로드캐스트 리시버 이해하기  (0) 2023.07.03
섹션 0. 들어가기에 앞서  (0) 2023.07.02
13-5 액티비티 ANR 문제와 코루틴  (0) 2023.07.01