기기를 켜진 상태로 유지

배터리가 많이 소모되지 않도록 유휴 상태의 Android 기기는 빠르게 절전 모드로 전환됩니다. 그러나, 애플리케이션이 일부 작업을 완료하기 위해 화면 또는 CPU를 절전 모드에서 해제하고 켜진 상태로 유지해야 하는 경우도 있습니다.

이를 위해 취하는 접근 방식은 앱의 필요에 따라 다릅니다. 하지만 일반적인 규칙은 앱이 시스템 리소스에 미치는 영향을 최소화할 수 있는 가능한 한 가장 가벼운 방식을 사용하는 것입니다. 다음 섹션에서는 기기의 기본 절전 동작이 앱의 요구 사항과 충돌하는 경우 이를 처리하는 방법을 설명합니다.

wake lock 사용의 대안

앱에 wake lock 지원을 추가하기 전에 앱의 사용 사례가 다음 대안 중 하나를 지원하는지 살펴보세요.

  • 앱이 장시간 HTTP 다운로드를 실행한다면 DownloadManager의 사용을 고려해보세요.
  • 앱이 외부 서버의 데이터를 동기화한다면 동기화 어댑터를 만드는 것이 좋습니다.
  • 앱이 백그라운드 서비스에 의존한다면 JobScheduler 또는 Firebase 클라우드 메시징을 사용하여 특정 간격으로 이러한 서비스를 트리거할 수 있습니다.

화면을 켜진 상태로 유지

게임이나 영화 앱 같은 특정 앱은 화면이 켜진 상태를 유지해야 합니다. 이를 위해 가장 좋은 방법은 활동에 FLAG_KEEP_SCREEN_ON을 사용하는 것입니다(활동에만 사용하고 서비스나 다른 앱 구성요소에는 절대 사용하면 안 됨). 예를 들면 다음과 같습니다.

Kotlin

    class MainActivity : Activity() {

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        }
    }
    

자바

    public class MainActivity extends Activity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
      }
    }
    

이 접근 방식의 장점은 wake lock(CPU를 켜진 상태로 유지 참조)과 달리 특정 권한이 필요하지 않고 앱이 사용하지 않는 리소스를 해제할 필요 없이 사용자가 애플리케이션 간에 이동할 수 있도록 플랫폼이 올바르게 관리한다는 것입니다.

또 다른 방법으로 애플리케이션의 레이아웃 XML 파일에 android:keepScreenOn 속성을 사용하여 다음과 같이 구현할 수도 있습니다.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:keepScreenOn="true">
        ...
    </RelativeLayout>

android:keepScreenOn="true"를 사용하는 것은 FLAG_KEEP_SCREEN_ON을 사용하는 것과 동일합니다. 어떤 접근 방식이든 앱에 가장 최선인 방식을 사용하면 됩니다. 활동에 프로그래밍 방식으로 플래그를 설정하는 것은 나중에 프로그래밍 방식으로 플래그를 삭제하여 화면이 꺼지도록 선택할 수 있는 장점이 있습니다.

참고: 실행 중인 애플리케이션에서 계속 화면이 켜져 있을 필요가 없다면(예를 들어, 특정 시간 동안 활동이 없을 때 화면을 타임아웃하려는 경우) FLAG_KEEP_SCREEN_ON 플래그를 삭제하지 않아도 됩니다. 창 관리자는 앱이 백그라운드로 이동하거나 포그라운드로 돌아갈 때 올바른 작업을 하도록 확인합니다. 하지만, 명시적으로 플래그를 삭제하여 화면이 꺼지도록 하려면 다음과 같이 clearFlags()를 사용합니다. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

CPU가 켜진 상태를 유지

기기가 절전 모드로 전환되기 전에 일부 작업을 완료하기 위해 CPU를 실행 중인 상태로 유지하려면 wake lock이라는 PowerManager 시스템 서비스 기능을 사용합니다. wake lock을 사용하면 애플리케이션에서 호스트 기기의 전원 상태를 제어할 수 있습니다.

wake lock을 생성하고 유지하는 것은 호스트 기기의 배터리 수명에 심각한 영향을 미칠 수 있습니다. 따라서, wake lock은 꼭 필요한 경우에만 사용하고 가능한 한 짧은 시간 동안 유지해야 합니다. 예를 들어 활동에서 wake lock을 사용할 필요는 없습니다. 위에서 설명한 것처럼 활동에서 화면을 계속 켠 상태로 유지하려면 FLAG_KEEP_SCREEN_ON을 사용하세요.

화면이 꺼진 상태에서 CPU를 계속 실행하여 작업을 처리하도록 wake lock을 적용해야 하는 백그라운드 서비스에 wake lock을 사용하면 좋습니다. 하지만, 배터리 수명에 미치는 영향을 고려하여 이런 경우도 최소화해야 합니다.

Wake lock을 사용하려면 먼저 다음과 같이 애플리케이션의 manifest 파일에 WAKE_LOCK 권한을 추가합니다.

<uses-permission android:name="android.permission.WAKE_LOCK" />

앱이 특정 작업용 서비스를 사용하는 broadcast receiver를 포함하고 있다면 기기를 켜진 상태로 유지하는 broadcast receiver 사용에 설명한 대로 WakefulBroadcastReceiver를 사용하여 wake lock을 관리할 수 있습니다. 이 방법을 사용하는 것이 좋습니다. 앱이 이 패턴을 따르지 않는 경우 다음과 같이 wake lock을 직접 설정할 수 있습니다.

Kotlin

    val wakeLock: PowerManager.WakeLock =
            (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
                newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp::MyWakelockTag").apply {
                    acquire()
                }
            }
    

자바

    PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
    WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
            "MyApp::MyWakelockTag");
    wakeLock.acquire();
    

Wake lock을 해제하려면 wakelock.release()를 호출합니다. 그러면 CPU 소유권 주장이 취소됩니다. 배터리 소모를 피하기 위해 앱이 종료되자마자 이 메서드를 사용하여 wake lock을 해제하는 것이 중요합니다.

기기를 켜진 상태로 유지하기 위해 broadcast receiver 사용

서비스와 함께 broadcast receiver를 사용하면 백그라운드 작업의 수명 주기를 관리할 수 있습니다.

WakefulBroadcastReceiver는 앱의 PARTIAL_WAKE_LOCK을 생성 및 관리하는 broadcast receiver의 특별한 유형입니다. WakefulBroadcastReceiver는 작업을 Service(일반적으로 IntentService)로 전달하면서 전환 시 기기가 절전 모드가 되지 않도록 합니다. 작업을 서비스로 전환하는 동안 wake lock을 유지하지 않는다면 작업이 완료되기 전에 기기가 절전 모드로 돌아가도록 효과적으로 허용하는 것입니다. 이에 따라 의도와 달리 앱에서 미래의 임의 시점까지 작업이 끝나지 않을 수도 있습니다.

WakefulBroadcastReceiver를 사용하는 첫 번째 단계는 다른 broadcast receiver와 같이 다음을 manifest에 추가하는 것입니다.

<receiver android:name=".MyWakefulReceiver"></receiver>

다음 코드는 startWakefulService() 메서드와 함께 MyIntentService를 시작합니다. 이 메서드는 서비스가 시작할 때 WakefulBroadcastReceiver가 wake lock을 유지하고 있는 경우를 제외하고 startService()와 유사합니다. startWakefulService()와 함께 전달된 인텐트는 추가로 식별한 wake lock을 유지합니다.

Kotlin

    class MyWakefulReceiver : WakefulBroadcastReceiver() {

        override fun onReceive(context: Context, intent: Intent) {

            // Start the service, keeping the device awake while the service is
            // launching. This is the Intent to deliver to the service.
            Intent(context, MyIntentService::class.java).also { service ->
                WakefulBroadcastReceiver.startWakefulService(context, service)
            }
        }
    }
    

자바

    public class MyWakefulReceiver extends WakefulBroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {

            // Start the service, keeping the device awake while the service is
            // launching. This is the Intent to deliver to the service.
            Intent service = new Intent(context, MyIntentService.class);
            startWakefulService(context, service);
        }
    }
    

서비스가 종료되면 MyWakefulReceiver.completeWakefulIntent()를 호출하여 wake lock을 해제합니다. completeWakefulIntent() 메서드는 다음과 같이 WakefulBroadcastReceiver에서 전달된 인텐트와 동일한 인텐트를 매개변수로 갖습니다.

Kotlin

    const val NOTIFICATION_ID = 1

    class MyIntentService : IntentService("MyIntentService") {

        private val notificationManager: NotificationManager? = null
        internal var builder: NotificationCompat.Builder? = null

        override fun onHandleIntent(intent: Intent) {
            val extras: Bundle = intent.extras
            // Do the work that requires your app to keep the CPU running.
            // ...
            // Release the wake lock provided by the WakefulBroadcastReceiver.
            MyWakefulReceiver.completeWakefulIntent(intent)
        }
    }
    

자바

    public class MyIntentService extends IntentService {
        public static final int NOTIFICATION_ID = 1;
        private NotificationManager notificationManager;
        NotificationCompat.Builder builder;
        public MyIntentService() {
            super("MyIntentService");
        }
        @Override
        protected void onHandleIntent(Intent intent) {
            Bundle extras = intent.getExtras();
            // Do the work that requires your app to keep the CPU running.
            // ...
            // Release the wake lock provided by the WakefulBroadcastReceiver.
            MyWakefulReceiver.completeWakefulIntent(intent);
        }
    }