한 걸음 두 걸음

안드로이드 시스템 프로그래밍 #07 ] Power 본문

FrontEnd/mobile system programming

안드로이드 시스템 프로그래밍 #07 ] Power

언제나 변함없이 2019. 4. 10. 09:15
반응형

배터리란

물질의 화학적 혹은 물리적 반응을 이용하여 이들의 변화로 방출되는 에
너지를 전기 에너지로 변환하는 소형 장치(이온화 경향이 다른 두 금속 간 산화 환원 반응 및 이 과정에서 물질 간 이동하는 전자의 흐름을 이용)

이온화 경향: 전자를 버리려는 성질의 정도,
산화: 전자를 잃는 것
환원: 전자를 얻는 것

화학전지

1차 전지: 일회용 배터리

  • 리튬 망간
  • 알카리 망간

2차 전지: 충전 가능한 배터리

  • 납축 전지
  • 니켈 카드뮴, 니켈 아연, 니켈 수소
  • 리튬 이온
  • 리튬(이온) 폴리머(아이폰 배터리)
  • 셀 디자인이 쉬움
    (모양을 비교적 자유롭게 만들 수 있다,
    전해질이 준고체 상태이기 때문에 용액이 잘 새어 나오지 않는다.)

배터리 용량

단위: mAh
: 한 시간 동안 표시된 숫자만큼의 전류를 제공할 수 있다는 의미
(한 시간 최대로 공급할 수 있는 전류가 나와있는 것일 뿐 한 시간마다 그렇게 소비하는 것이 아님.)

Power파워

파워란 물리학에서 단위 시간당 일의 양(일률)으로 정의, 전기 관점에서 전력이다.
전력은 단위 시간당 전류가 할 수 있는 일의 양(* 1W = 1V x 1A)이다.
(전력량은 일정한 시간 동안 사용한 전력의 양으로 1kWh는 1kW 소비전력을 갖는 전기 제품을 1시간 동안 사용했을 때의 전력량이다.)

배터리사용시간

3.7V, 1800mAh 용량을 가진 핸드폰이 600mA자원을 사용하는 애플리케이션을 사용한다면 3시간동안 사용할 수 있겠죠 : )

파워 관리

배터리 용량도 꾸준히 늘어나고 있지만, 보다 더 핸드폰에서 자원을 소모하는 속도가 더 빠르게 늘어나고 있어서 이를 효율적으로 관리하기 위한 H/W S/W로 바뀌고 있습니다.

HW
CPU / GPU 등에 DVFS(Dynamic Voltage and Frequency Scaling : 연산량에 따라 전압과 주파수를 조절한다.)를 적용시키거나, Heterogeneous computing architecture(ARM에서 개발한 컴퓨터 아키텍쳐로, 상대적으로 전력 소모가 적은 저성능 프로세서 코어(LITTLE)와
전력 소모가 많은 고성능 코어(big)를 함께 탑재하여 동적으로 할당처리합니다. )
,異기종
SW
androidAPP의 경우 백그라운드 실행제한하거나 브로드캐스트 제한을 하거나 수명주기(onCreate-onStart-onResume-onPause-onStop-onDestroy)로 자원을 효율적으로 관리합니다. 뿐만 아니라,


모바일기기의 상태(Run, Standy(시스템 비활성화), Sleep(Stanby상태 지속될 경우), Shutdown(시스템off))에 따라 파워를 관리합니다.

예외 위의 규칙에 따르지 않는 앱이 있다. (동영상, 네비게이션, background 뮤직플레이어 등) 이는 화면을 터치하지 않거나 화면이 꺼지더라도 작업을 수행해야한다.

PowerManager 클래스

안드로이드 디바이스의 파워 상태를 제어할 수 있도록 하는 API이다.
manifest permission :

Context.getSystemService() 메소드를 이용하여 PowerManager 객체를 생성

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

PowerManager의 newWakeLock() 메소드를 이용하여 PowerManager.WakeLock 객체 생성

PowerManager.WakeLock wl =  pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"Tag: partial wake lock");

• WakeLock의 메소드를 이용하여 파워 상태(주로 CPU)를 제어합니다.
• acquire(), release() : 반드시 필요한 경우에만 wakelock을 사용하고, 가장 낮은 레벨로 사용하며, 사용 후 반드시 release 해야 함

wl.acquire();
wl.release();

참고 URL : https://developer.android.com/reference/android/os/PowerManager.html?hl=ko

newWakeLock 메소드

public PowerManager.WakeLock newWakeLock (int levelAndFlags, String tag);

WakeLock 객체를 생성하여 내장된 newWakeLock 메소드를 호출하여 사용합니다.

PowerManager.WakeLock wl =
pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK|PowerManager.ON_AFTER_RELEASE, TAG);

levelAndFlags

  1. level (하나의 레벨만 사용합니다.)

    • PARTIAL_WAKE_LOCK의 경우 디스플레이 타임아웃이나 스크린 상태에 상관 없이 CPU가 계속 on
    (사용자가 파워 버튼을 눌러서 화면을 끄더라도 CPU는 계속 돌아간다)
    다른 wake lock의 경우 파워 버튼을 누르면 sleep 상태로 변경된다
    사용자가 파워 버튼을 누르면, 시스템에 의해서 lock이 release됨. screen과 CPU는 off
    • ACQUIRE_CAUSES_WAKE_UP
    보통 wake lock은 켜진 화면을 계속 켜져 있게는 하지만 꺼진 화면을 강제로 켜지는 않는다
    이 플래그가 사용되면 wake lock이 acquire() 되면 스크린과 키보드가 바로 켜진다
    notification 등이 있을 때 주로 사용
    • ON_AFTER_RELEASE
    wake lock이 release된 후 화면 타이머를 reset하여 화면이 꺼지는 시간을 좀 늦춘다

  1. flag((PowerManager 클래스에 정의된 constant))
    level과 OR operator를 사용하여 함께 사용합니다.
    스크린 동작에만 영향, partial wake lock의 사용에는 아무 영향 없음
    • ACQUIRE_CAUSES_WAKEUP
    • ON_AFTER_RELEASE

화면제어

화면과 관련된 wake lock은 현재 deprecated
• FULL_WAKE_LOCK
• SCREEN_DIM_WAKE_LOCK
• SCREEN_BRIGHT_WAKE_LOCK

wake lock을 안 쓰고 화면을 on 상태로 유지하는 방법
Activity에서 FLAG_KEEP_SCREEN_ON 사용( service 혹은 다른 app component에서 사용하면 안 됨) -> permission을 필요로 하지 않음, 안드로이드 플랫폼이 알아서 관리해줌 (app에서 명시적으로 release 하지 않아도 됨)

 1. Activity onCreate에서 코드로 설정하는 방법

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

2. 레이아웃 XML 파일에서 설정하는 방법

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

Counter 실습!

  1. menifest에 퍼미션추가.

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/ms_1000_countdown_text"
        android:layout_gravity="center_horizontal"
        android:text="Count: 0"
        android:textSize="40sp"/>

    <Button
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start Count"
        android:onClick="onClick"/>

    <Button
        android:id="@+id/stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop Count"
        android:onClick="onClick"/>


</LinearLayout>
  1. MainActivity.java
import android.content.Context;
import android.os.PowerManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity {

    TextView m1000msCountTv = null;

    Timer mTimer = new Timer();
    TimerTask m1000msCountTimerTask = null;

    //***********************************
    // wake lock을 사용하는 경우 필요한 코드
    PowerManager pm;
    PowerManager.WakeLock wl;
    //***********************************

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        m1000msCountTv = (TextView)findViewById(R.id.ms_1000_countdown_text);

        //**************************************************************
        // wake lock을 사용하는 경우 필요한 코드
        pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Tag: partial wake lock");
        //wl.acquire();
        //**************************************************************
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // 타이머는 작업 스레드이기 때문에 액티비티가 종료될 때
        // 반드시 중단하여 스레드를 제거시키도록 한다
        mTimer.cancel();

        //***********************************
        // wake lock을 사용하는 경우 필요한 코드
        //wl.release();
        //***********************************
    }

    public void onClick(View v) {
        switch(v.getId()) {
            case R.id.start:
            {
                startTimerTask();
                break;
            }
            case R.id.stop:
            {
                stopTimerTask();
                break;
            }
        }
    }

    private void startTimerTask() {
        // 1. TimerTask 실행 중이라면 중단한다
        stopTimerTask();

        // 2. TimerTask 객체를 생성한다
        // 1000 밀리초마다 카운팅 되는 태스크를 등록한다
        m1000msCountTimerTask = new TimerTask() {
            int mCount = 0;
            @Override
            public void run() {
                mCount++;
                m1000msCountTv.post(new Runnable() {
                    @Override
                    public void run() {
                        m1000msCountTv.setText("Count: " + mCount);
                    }
                });
            }
        };

        // 3. TimerTask를 Timer를 통해 실행시킨다
        // 1초 후에 타이머를 구동하고 1000 밀리초 단위로 반복한다
        // 	schedule(TimerTask task, long delay, long period)
        mTimer.schedule(m1000msCountTimerTask, 1000, 1000);
        //*** Timer 클래스 메소드 이용법 참고 ***//
        // http://developer.android.com/intl/ko/reference/java/util/Timer.html
        //***********************************//
    }

    private void stopTimerTask() {
        // 1. 모든 태스크를 중단한다
        if(m1000msCountTimerTask != null) {
            m1000msCountTimerTask.cancel();
            m1000msCountTimerTask = null;
        }

        // 2. 카운팅 초기화값을 텍스트뷰에 표시
        m1000msCountTv.setText("Count: 0");
    }
}

 


테스트! 

여기서 acquire()로 wakelock을 얻느냐 안얻느냐에 따라 달라지는 count값 확인하기.

(얻지 않았을 때 sleep상태가 되면 count가 더디게 올라가는데 얻었을 때는 꾸준하게 올라갑니다!)

(USB가 꽂혀있지않는 상태로 활용해줍니다 : ) usb 디버깅 모드 연결된 상태로 안드로이드를 실행시키면 자동으로 wake-lock이 잡히게되는 설정이 있기 때문입니다.)

반응형