본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[Android/안드로이드] PowerManager 를 통해 안드로이드의 진정한 주인이 되어 봅시다.

by 돼지왕왕돼지 2012. 1. 25.

 [Android/안드로이드] PowerManager 를 통해 안드로이드의 진정한 주인이 되어 봅시다.



PowerManager / 파워 매니져 / System Service / WakeLock / PowerManager.WakeLock /    Device / 단말 / 전원 / Power / 제어 / control / 배터리 사용 시간 / API / acquire /    release / 배터리 조루 / POWER_SERVICE  / getSystemService / newWakeLock / power flag / PARTIAL_WAKE_LOCK / SCREEN_DIM_WAKE_LOCK / SCREEN_BRIGHT_WAKE_LOCK / FULL_WAKE_LOCK / CPU / Screen / Keyboard / ACQUIRE_CAUSES_WAKE_UP / ON_AFTER_RELEASE / 깜박거림 / Notification / flicker / 수신 전화 / 알람 / permission / uses-permission / android.permission.WAKE_LOCK / Manifest / 매니페스트 / 권한 / goToSleep / isScreenOn / reboot / userActivity / isHeld / setReferenceCounted / setWorkSource / toString / Tutorial / 기초 강좌 / Android / 안드로이드 / 배터리 절약 정책 / 네비게이션 / Navigation / DMB / 동영상 플레이어 / 백그라운드 작업 / Background Process / LockScreen / 락스크린 / WifiLock / WifiManager



1. Information


먼저 Developer 의 내용을 간단히 정리해주세요


- PowerManager 는 Device 의 전원 상태 ( Power state ) 를 제어할 수 있게 도와줍니다.

- 단말의 배터리 사용시간은 PowerManager 의 API 사용에 따라 현저한 차이를 나타냅니다.

- 반드시 필요한 경우가 아니라면 WakeLocks 을 acquire 하지 않는 것이 좋습니다.

- 가능한 낮은 레벨을 사용하고 사용 후에는 반드시 release 하는 것이 좋습니다.


Developer 에서 class 에 대한 소개보다도 주의사항을 더 많이 표시하고 있습니다.
이 말은, 잘만 사용하면 원하는 효과를 볼 수 있지만, 잘못 사용하면 소위 말하는 배터리 조루를 초래할 수 있다는 이야기지요.
사용할 때 주의하셔야 겠습니다.



언제 사용하나요?


 안드로이드 시스템은 사용자의 Interaction이 없을 때 화면이나 키보드 조명을 차단하고, Sleep 후 일정 시간이 지나면  WiFi 네트워크도 끊어버리며, CPU 까지도 잠재워버리는 배터리 절약 정책을 가지고 있습니다.
대부분의 앱이 이 System 의 배터리 절약 정책을 따르고 있지만, 이 정책에 협조하지 않아야 하는 App 들도 존재합니다.
먼저 동영상 플레이어가 그 예인데, 배터리가 적다고, 그리고 화면을 터치하지 않는다고 화면을 끄면 안 되겠죠?
마찬가지로 네비게이션이나 DMB 도 User Interaction 이 없다고 화면을 끄면 안되죠. 
백그라운드 작업을 하는 앱들은 화면이 꺼지더라도 CPU는 계속 동작해야 합니다. 

이러한, 예외적인 경우에는 안드로이드 자체의 배터리 절약 정책을 따를 수 없다고, 공식적으로 표명하고 따르지 않는 방법이 있는데,
그것을 도와주는 것이 PowerManager 와 PowerManager.WakeLock 입니다.





어떻게 사용하나요?


 먼저 PowerManagerSystem Service로서 다음과 같이 얻어올 수 있습니다.

PowerManager pm = (PowerManager) getSystemService( Context.POWER_SERVICE );


 가장 많이 사용하는 API 는 newWakeLock() 이고 return 값은 PowerManager.WakeLock object 이죠.
이 WakeLock 이라는 object 를 이용하여 power control 을 할 수 있는 것입니다.

PowerManager.WakeLock wakeLock = pm.newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK, "MY TAG" );
wakeLock.acquire();
  // do something. 
  // the screen will stay on during this section.
wakeLock.release();


 Developer 에 명시되어 있듯이, 구현시에 꼭 필요한 경우가 아니라면 acquire() 하지 않는 것이 좋으며,
사용해야만 할 경우에는 반드시 사용이 끝나자마자 release() 를 해주어야 합니다.
또한 자신이 원하는 구현에서 꼭 필요한 내용만을 담고 있는 "가장 낮은 레벨" 을 사용해야 베터리를 아낄 수 있죠.



레벨 레벨 하시는데, 무슨 레벨들이 있나요?


 Flag Value CPU  Screen  Keyboard 
 PARTIAL_WAKE_LOCK  On   Off   Off 
 SCREEN_DIM_WAKE_LOCK  On   Dim   Off 
 SCREEN_BRIGHT_WAKE_LOCK  On   Bright   Off 
 FULL_WAKE_LOCK  On   Bright   Bright

Flag 들이 영향을 미치는 녀석들은 CPU, Screen, Keyboard (조명) 입니다.

위에 제시된 4개의 Flag 는 "|" 로 중첩하여 사용할 수 없습니다.

PARTIAL_WAKE_LOCK 의 경우 User 가 Power Button 을 눌러 단말을 sleep 시켜도 계속 CPU 가 돌게 되어 있습니다.
다른 녀석들은 Power Button 을 눌러 단말을 sleep 시키면 CPU 가 멈춥니다.
따라서 PARTIAL_WAKE_LOCK 을 acquire() 했을 때는 release() 가 더더더욱 더 중요하게 됩니다.


PARTIAL_WAKE_LOCK 을 제외한 나머지 3개의 Flag 에 대해서는 다음의 Flag를 "|" 로 더하여 사용할 수 있습니다.

 Flag Value  Description 
 ACQUIRE_CAUSES_WAKE_UP  보통의 WAKE_LOCK 은 켜진 화면을 계속 유지하게는 하지만, 꺼진 화면을 강제로 켜지는 않습니다. 하지만 이 Flag 가 함께 사용된다면, WakeLock이 acquire() 되는 순간 Screen 과 Keyboard 가 바로 켜집니다. 보통 긴급한 notification (착신전화, 알람) 등이 있을 때 주로 사용됩니다.
 ON_AFTER_RELEASE  이 Flag 가 설정되면 WakeLock 이 release 된 후 화면 timer를 reset 합니다. 이 때 화면이 꺼지는 시간이 조금 더 길어지기 때문에, WAKE_LOCK 조건을 여러 번 사용할 때 너무 빨리 꺼지지 않게 하여, 화면 깜빡임을 덜어줍니다.






가장 중요한 뭔가를 놓친 기분이에요..


 네. 정확하게 짚어내셨습니다.

이 PowerManager 의 WakeLock 을 사용하기 위해서는

Manifest에 <uses-permission> 을 이용해 android.permission.WAKE_LOCK 의 permission을 부여해야 합니다.

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


이게 설정되어 있지 않으면, 다 말짱 꽝입니다.



다른 API 들은 없나요?


 왜 없겠습니까? 다음과 같은 API 들을 제공합니다.
이름만 봐도 무슨 기능인지 아시겠죠? 자세한 것은 developer 를 참조하세요.

<PowerManager>

void goToSleep( long time )
boolean isScreenOn()
PowerManager.WakeLock newWakeLock( int flags, String tag )
void reboot( String reason )
void userActivity( long when, boolean noChangeLights )


[goToSleep() 을 사용하기 위해서는..]



<PowerManager.WakeLock>

void acquire()
void acquire( long timeout )
boolean isHeld()
void release()
void setReferenceCounted( boolean value )
void setWorkSource( WorkSource ws )
String toString()

 



2. Summary


- PowerManager 는 Device 의 전원 상태를 제어할 수 있도록 도와주는 system service class 이다.

- PowerManager 는 배터리 사용시간을 좌지우지하는 녀석으로 주의해서 사용해야 한다. 반드시

- 필요한 경우가 아니라면 WakeLock 을 acquire 하지 않는 것이 좋으며, acquire 를 했으면, 사용이 끝나자마자 바로 release 해주어야 한다.

- newWakeLock() 의 flag 로 사용할 수 있는 녀석은 총 4가지가 있으며 PARTIAL_WAKE_LOCK 을 제외한 3가지 경우는 2가지 flag 를 추가로 | 로 중첩시킬 수 있다.

- PowerManager 의 WakeLock 을 사용하기 위해서는 android.permission.WAKE_LOCK 을 permission 으로 주어야 한다.




3. References


- http://developer.android.com/reference/android/os/PowerManager.html 
   Android Developer Doc. PowerManager

http://developer.android.com/reference/android/os/PowerManager.WakeLock.html 
   Android Developer Doc. PowerManager.WakeLock

http://developer.android.com/reference/android/net/wifi/WifiManager.html 
   Android Developer Doc. WifiManager

http://developer.android.com/reference/android/net/wifi/WifiManager.WifiLock.html 
   Android Developer Doc. WifiManager.WifiLock







댓글17

  • 질문 있어요 2012.12.18 10:38

    왜 Keyboard가 조명이죠??? 잘 이해가 안갑니다. LED로 생각하면 될까요?
    답글

    • 네에~ qwerty 키보드가 달린 옵티머스 Q 같은 녀석들은, 키보드에 조명이 있는 경우가 많습니다. 우리가 터치로 이용하는 soft keyboard 가 아닌 hardware keyboard 를 말하는거예요. 이해가 가시나용??

  • 2013.06.13 04:33

    비밀댓글입니다
    답글

  • yb2k 2013.07.11 10:28

    슬립모드에 들어가기전에 PARTIAL_WAKE_LOCK을 키면 CPU가 계속 살아있게 되는거 같은데

    이미 슬립모드에 들어간 상태에서 서비스에서 PARTIAL_WAKE_LOCK를 구동하면 어떻게 되나요?

    죽은 CPU도 살아나나요?
    답글

    • 네, 직접 해보진 않았지만 reference 에 따르면 슬립모드 상태에서도 이 녀석을 호출하게 해주면 죽은 CPU 도 살아날 것 같네요. 근데 엄밀히 말하면 CPU 는 죽은게 아니라, idle 로 잠시 쉬는 거죠.

  • yeon 2017.05.31 16:50

    친절한 설명 갑사드려요! 많이 배우고 가요!!
    답글

  • 대학생1 2017.11.11 22:49

    안녕하세요 현재 안드로이드 스튜디오를 공부하고 있는 대학생입니다..! 제가 PowerManager의 Screeon()을 이용해서 화면이 켜지면 스톱워치가 지속되고 꺼지면 스톱워치가 멈추도록, 즉 화면이 켜져있을때만 스탑워치가 가서 그 시간을 측정하도록 하는 어플을 구현하려 하는데.. 아무리 해봐도 이 스톱워치가 멈추지 않고 계속 가네요.. 제가 이해하고 코딩하는 방식이 잘못된 것인지 조언을 구해서 알고 싶습니다.
    PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);

    boolean isScreenOn = pm.isScreenOn();
    if(isScreenOn)
    {
    light.setText("light!");
    startTime = SystemClock.uptimeMillis();
    customerHandler.postDelayed(updateTimeThread, 0);
    }
    else
    {
    light.setText("not light");
    timeSwapBuff += timeInMilliseconds;
    customerHandler.removeCallbacks(updateTimeThread);
    }

    };

    그리고 코드부분에서 isScreenOn 이 부분이 가운데 줄이 가있던데 이것은 제 기능을 할 수 없다는 뜻인건가요..?
    답글

    • 안녕하세요 대학생1님.
      답변이 조금 늦었네요~

      우선 위와 풀 코드가 없어서 왜 스톱워치가 멈추지 않는지 정확한 분석은 어렵습니다.
      우선 저 isScreenOn 이라는 것을 주기적으로 체크해야 정상작동할 것 같은데 위 코드에는 주기적 체크를 하는지 알 수 없네요.

      두번째로 줄이 가 있다고 하셨는데 확인해보니 deprecated 된 API 네요
      isInteractive() 를 대신쓰라고 안내되어 있으니 아래 링크를 통해 API 설명을 한 번 읽어보시고 대체해서 사용하시면 되겠습니다.
      https://developer.android.com/reference/android/os/PowerManager.html#isScreenOn()

      더 궁금한 점이 있으면 댓글 주세요 :)

  • 대학생1 2017.11.13 17:28

    답변 감사합니다..! 몇일간 여기에 막혀서 진전없이 시간만 보내고 있던 저에게는 희망같은 답변입니다..ㅜ
    제가 먼저 스톱워치로 짠 코드먼저 보여드리겠습니다..!

    long startTime=0L,timeInMilliseconds=0L,timeSwapBuff=0L,updateTime=0L;
    int mins, secs = 0;

    Runnable updateTimeThread = new Runnable() {
    @Override
    public void run() {
    timeInMilliseconds = SystemClock.uptimeMillis() - startTime;
    updateTime = timeSwapBuff + timeInMilliseconds;
    secs = (int) (updateTime / 1000);
    mins = secs / 60;
    secs %= 60;
    int milliseconds = (int) (updateTime % 1000);
    txtTimer.setText("" + mins + ":" + String.format("%2d", secs) + ":" + String.format("%3d", milliseconds));
    customerHandler.postDelayed(this, 0);

    }
    };
    기본적인 스톱워치코드이며 OnCreate() 밖에 작성하여 핸들러로 호출해서 스톱워치가 돌아가는 형식으로 작성했습니다. 코드는 이전에 질문했던 것과 이것이 전부입니다. 답변에서 주기적으로 체크를 하여야 한다고 하셨는데 주기적으로 해보려고 타이머를 사용하는 방식으로 코드를 작성해서 해보았으나 이 경우 만약 화면이 켜져있는 경우에도 계속 체크를 하여 타이머를 실행하도록 호출해버려서 엉켜버리게 됬습니다.. 그래서 생각한게 주기적으로 체크할 수 있도록 감지는하지만, 화면이 켜지고 꺼질때의 액션이 있을때 코드를 실행할 수 있도록 하여야 할 것 같은데 제가 아는 지식의 한계로는 어떤 것을 사용하여야 할지 감이 잡히지 않습니다.. 아, 그리고 isScreenOn 대신에 isInteractive()로 교체하여 사용하는건 돼지님 도움으로 해결되었습니다 감사합니다..! 이렇게 막막한 질문을 드리는데 친절히 답변해주셔서 감사합니다, 몇일간 계속 한자리만 맴돌고 있는데 마치 누가 길을 알려준거 같았습니다, 어떤 조언이라도 감사히 받겠습니다..!

    아 그리고 추가적으로 질문이 하나 더 있는데 OnCreate()는 어플 실행시 처음 한번만 실행되는 것으로 알고 있는데 제가 알고있는게 맞는건가요?
    답글

    • 우선.. Activity lifecycle 규칙에 따르면 onStart 되었을 때가 화면에 노출될 때, onStop 이 화면에서 노출되지 않을 때로 rough 하게 매칭이 됩니다.

      onStart 에서 stopWatchRunnable 을 등록해서 시간 표시하는 View 를 계속 업데이트 해주고, onStop 에서 stopWatchRunnable 을 제거해주면 구현이 완료되지 않을가 싶습니다.

      원하시는게 이 방향이 아니라면..
      stopWatchRunnable 안에서 isScreenOn(isInteractive) 을 계속 체크해주어야 합니다.
      screenOn, Off 상태가 바뀐다고 if(pm.isScreenOn) else 구문이 자동으로 타지 않습니다.

      참고가 되시길 :)

  • 대학생1 2017.11.14 18:45

    Runnable updateTimeThread = new Runnable() {
    @Override
    public void run() {
    timeInMilliseconds = SystemClock.uptimeMillis() - startTime;
    updateTime = timeSwapBuff + timeInMilliseconds;
    secs = (int) (updateTime / 1000);
    mins = secs / 60;
    secs %= 60;
    int milliseconds = (int) (updateTime % 1000);
    txtTimer.setText("" + mins + ":" + String.format("%2d", secs) + ":" + String.format("%3d", milliseconds));
    if(isScreenOn=true)
    customerHandler.postDelayed(this, 0);
    else
    customerHandler.removeCallbacks(this);
    };

    이 아래 코드는 OnCreate()안에 코딩한 PowerManager 입니다..
    PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);

    isScreenOn = pm.isInteractive();
    if(isScreenOn)
    {
    light.setText("light");
    startTime = SystemClock.uptimeMillis();
    customerHandler.postDelayed(updateTimeThread, 0);
    }
    답변 감사합니다..! 답변해주신 첫번째 방식은 코딩해보고 구동하는 것도 확인을 했습니다..!
    하지만 lifecycle을 이용하게되면 화면이 노출되지 않을때 멈추지만 나중에 백그라운드 구동도 생각하고있어서 어플화면에서 홈화면으로 갔을때도 멈추게 되어 원하던 방향이 아닌 것 같습니다.. ㅜ
    그래서 두번째 방법으로 코딩을 위와 같이 했는데 멈추지 않고 그대로 타이머가 멈추지 않아 이렇게 다시 질문 드립니다..!

    답글

    • 음.. 우선 이런방식으로 컴하는것은 끝이 없을것 같은데요..

      코드를 짜드려야하는지.. 어찌 도움을 더 드려야할지 감이 잘 안 잡히네요 ^^;;

      그리고 원하는 바가 screen on off 인지 activity visibility 인지 확실히 알아야 할 것 같고, 최종적으로 구현하고싶은 내용도 알아야할 것 같습니다

  • 대학생1 2017.11.14 20:15

    아.. 제 질문이 너무 두서 없이 막 흘러갔네요.. ㅜㅜ 몇일간 아무것도 못하고 이 코딩에 막혀있어서 너무 답답한 마음에
    너무 두서없이 질문드렸습니다ㅜ 이미 충분히 도와주시고 조언해주신 것 생각하면서 이제는 제 스스로 더 노력해볼게요! 조언해주신 것들 넘치도록 도움 됬습니다..! 이런 갑작스런 질문에도 친절히 답변해주셔서 정말 감사하고 포스팅 하시는 글들 잘 보면서 노력하겠습니다 ! 정말 감사합니다
    답글

  • 2017.11.20 18:21

    비밀댓글입니다
    답글