Keeping the Device Awake

배터리가 소모되는 것을 막기 위하여 idle 상태의 안드로이드 단말은 금방 sleep 상태로 변한다. 하지만 가끔 애플리케이션 작동을 위해 스크린이나 CPU를 어웨이크 상태로 유지해야하는 경우가 있다. 물론 이에 대한 접근법은 서비스의 특성에 따라 달라지겠지만 마구잡이로 어웨이크 상태로 만드는 것은 시스템 리소스에 부담을 줄 수 있다. 이에 리소스에 대한 영향을 최소화하기 위해 앱을 최적화하는 방법을 찾아야 하는 것은 당연한 일이다. 이번 장에서는 디바이스의 sleep과 awake의 상태변화가 앱의 요구사항을 만족시키며 리소스까지 관리 할 수 있도록하는 방법에 대해 설명한다.

Keep the Screen On

게임이나 영화앱 등 일부 앱은 스크린이 켜진 상태를 유지해야 한다. 가장 좋은 방법은 액티비티에 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(아래 Keep the CPU On에서 설명할)과 달리 특별한 권한이 필요없고, OS가 리소스를 알아서 관리해주기 때문에 특별히 릴리즈할 필요가 없다는데 있다. 또다른 방법은 아래와 같이 해당 액티비티의 레이아웃 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 를 사용하는 것과 동일하다. 앱의 특성에 따라 무엇이 최선인지, 어떤 것을 이용할지는 개발자가 판단하여 선택하면 된다. 액티비티에서 addFlags() 를 통해 사용하는 방식의 장점은, 나중에 플래그를 클리어 함으로써 해당 옵션을 끌 수 있다는 점이다.

실행중인 앱의 화면에서 다른 화면으로 이동할 경우에도FLAG_KEEP_SCREEN_ON 를 클리어할 필요가 없다 (다른화면에선 일정 시간동안 활동이 없을경우 스크린이 자동적으로 꺼지게 될 것이다). 윈도우 매니저가 앱이 백그라운드로 전환하거나 포그라운드로 활성화 될때 올바른 액션이 일어나도록 관리한다. 하지만, 만약 명시적으로 플래그를 클리어하여 스크린을 끄고싶다면, 아래와 같이 clearFlags() 를 사용한다. getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

Keep the CPU On

단말이 sleep 모드로 전환되기 전에 몇가지 일을 실행하기 위해서 CPU가 계속 동작해야 한다면, wake locks라고 하는 PowerManager 시스템 서비스 기능을 사용할 수 있다. Wake locks는 호스트 단말의 전원 상태를 앱이 제어할 수 있도록 하는 기능을 가지고 있다.

그러나 Wake locks를 생성하여 사용하는 것은 호스트 단말의 배터리 수명에 큰 영향을 미칠 수 있다. 그러기 때문에 wake locks는 정말 반드시 필요할 때에만 사용하고, 최대한 적은 시간동안 유지할 수 있도록 한다. 예를 들어, 액티비티에서는 절대로 wake lock을 사용하지 않도록 한다. 위에 설명한 바와 같이, 만약 액티비티에서 스크린을 켜두고 싶다면 FLAG_KEEP_SCREEN_ON 를 사용하면 된다.

Wake lock을 사용할 수 있는 적절한 케이스 중 하나는 스크린이 꺼져있는 동안 CPU 동작을 유지시키기 위해 wake lock을 사용하는 경우이다. 하지만 다시 한 번 말하지만, 배터리 수명의 영향을 줄이기 위해 이 방법의 사용은 최소화되어야 한다.

wake lock을 사용하기 위한 첫단계는 앱의 매니페스트 파일에 WAKE_LOCK 퍼미션을 추가하는 것이다.

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

앱이 작업을 위해 서비스를 사용하는 브로드캐스트 리시버를 포함 한다면, 아래 Using a WakefulBroadcastReceiver에서 설명한 대로, WakefulBroadcastReceiver를 통해 wake lock을 관리하도록 한다 . 앱이 이 패턴을 따르지 않는다면, 다음 방법으로 wake lock을 직접 셋팅할 수 있다.

PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
Wakelock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, “MyWakelockTag”);
wakeLock.acquire();

CPU를 릴리즈 할 수 있도록 wakelock.release() 를 호출한다. 이는 배터리의 급격한 소모를 방지하기 위한 작업이기 때문에 앱이 완료되자 마자 wake lock을 릴리즈 하는것을 잊지말도록 하자.

Using WakefulBroadcastReceiver

서비스와 함께 브로드캐스트 리시버를 사용하면 백그라운드의 생명주기를 관리할 수 있다.

WakefulBroadcastReceive는 앱의 PARTIAL_WAKE_LOCK 을 생성하고 관리할 수 있는 특별한 브로드캐스트 리시버이다. WakefulBroadcastReceiver 는 서비스 (주로 IntentService)에 작업을 넘겨주고, 이러한 전환 과정에서 단말이 sleep 상태로 돌아가지 않도록 한다. 만약 서비스를 수행 할 때 wake lock을 유지하지 않는다면, 작업이 완료되기 전에 장치가 다시 sleep모드로 돌아가게된다. 이에 따라 앱이 작업을 완료하지 않게 될 수도있다.

WakefulBroadcastReceiver를 사용하는 첫 단계는, 다른 브로드캐스트 리시버에도 그러듯, 매니페스트에 등록을 하는 것이다.

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

다음 코드는 startWakefulService() 메소드로 MyIntentService 를 시작하는 것을 보여준다. 이 메소드는 startService() 와 유사하지만, WakefulBroadcastReceiver 는 서비스 시작과 함께 wake lock을 사용한다는 차이점이 있다. 참고로 startWakefulService() 로 넘겨진 인텐트는 wake lock의 사용여부를 식별하는 extra 값을 포함한다.

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);
}
}

서비스가 완료되었을 때, wake lock을 릴리즈하기 위해 MyWakefulReceiver.completeWakefulIntent() 를 호출한다. completeWakefulIntent() 메소드는 WakefulBroadcastReceiver 에서 넘겨진 인텐트를 파라미터로 사용한다.

public class MyIntentService extends IntentService {
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
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);
}
}
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.