2017년 10월 29일 일요일

doze mode와 standby 모드

doze mode와 standby 모드


예전에 잘동작하던 어플이 어느 순간 동작이 되지 않았던 증상이 없나요?
Android는 Android 6.0(API 레벨 23) 부터 Doze standby 모드가 추가 되었습니다. 구글 안드로이드 설명에 따르자면 아래와 같은 목적을 위해서 시스템 적으로 앱의 동작에 제한을 걸게 되었습니다.
기기를 오랫동안 사용하지 않는 경우에 앱의 백그라운드 CPU 및 네트워크 액티비티를 지연시켜 배터리 소모량을 줄입니다.

구글 참조
https://developer.android.com/training/monitoring-device-state/doze-standby.html

아래에 따르면 늦게 동작은 해도 동작을 안하는 앱은 없을것으로 예상이 되지만 많은 앱들이 백그라운드로 동작 되는경우도 있기 때문에 실제는 그렇지 않습니다.

앱 대기 모드 이해


앱 대기 모드는 사용자가 활발하게 사용하지 않는 경우, 시스템이 앱의 유휴 상태를 결정하게 합니다. 시스템은 사용자가 일정 시간 앱을 터치하지 않고 다음 중 어느 조건에도 해당하지 않으면 이 결정을 내립니다.
  • 사용자가 명시적으로 앱을 실행합니다.
  • 앱에 현재 포그라운드에 있는 프로세스가 있습니다(액티비티 또는 포그라운드 서비스 중 하나의 형태로, 또는 다른 액티비티나 포그라운드 서비스가 사용 중인 상태로).
  • 앱이 알림을 생성하여 사용자가 그것을 잠금 화면에서 보거나 알림 트레이에서 확인합니다.
사용자가 기기를 전원 공급 장치에 연결하면 시스템은 앱 대기 모드를 해제하기 때문에 앱은 자유롭게 네트워크에 액세스하고 보류 중인 모든 작업 및 동기화를 실행할 수 있습니다. 기기가 오랜 시간 동안 유휴 상태인 경우, 시스템에서는 유휴 앱에 하루에 한 번 정도 네트워크 액세스를 허용합니다.


doze mode : 잠자기 모드
app standby : 앱 대기 모드

AlarmManager example 

알람테스트 앱을 만들어 봤습니다.

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.darts.test.alarmfordozemode.alarmfordozemode">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name=".Alarm"></receiver>
    </application>

</manifest>


MainActivity.java
package com.darts.test.alarmfordozemode.alarmfordozemode;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    Alarm alarm = new Alarm();

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


Alarm.java
package com.darts.test.alarmfordozemode.alarmfordozemode;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

public class Alarm extends BroadcastReceiver
{
    final static int TIME = 10; // min
    @Override
    public void onReceive(Context context, Intent intent)
    {
        Log.e("ATest","Alarm");
        Toast.makeText(context, "Alarm", Toast.LENGTH_LONG).show();
        setAlarm(context);
    }

    public void setAlarm(Context context)
    {
        AlarmManager am =( AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        Intent i = new Intent(context, Alarm.class);
        PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
        //am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+TIME*1000*60, pi);
        //am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+TIME*1000*60, pi);
        am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+TIME*1000*60, pi);
        // setAndAllowWhileIdle() 및 setExactAndAllowWhileIdle()
    }

    public void cancelAlarm(Context context)
    {
        Intent intent = new Intent(context, Alarm.class);
        PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(sender);
    }
}

아래 부분의 시간을 변경해서 테스트를 합니다.
final static int TIME = 10;  

아래 부분의 코드를 변경해 가면서 테스트를 진행하였습니다.
//am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+TIME*1000*60, pi);
//am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+TIME*1000*60, pi);
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+TIME*1000*60, pi);
// setAndAllowWhileIdle() 및 setExactAndAllowWhileIdle()


테스트 결과

set, TIME 1 분

TIME = 1;
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+TIME*1000*60, pi);
시간이 약간 달라질 수 있음
10-29 18:14:05.031 13467-13467/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:15:29.760 13467-13467/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:16:38.518 15236-15236/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:17:39.085 15236-15236/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:18:43.946 15236-15236/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:20:14.857 15236-15236/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:21:15.481 15236-15236/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm

setExact, TIME 1 분

TIME = 1;
am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+TIME*1000*60, pi);
정확한 시간에 알람이 발생함
10-29 18:23:50.226 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:24:50.327 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:25:50.406 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:26:50.481 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:27:50.539 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm

잠자기 모드에서 테스트

heroltelgt:/ $ dumpsys battery unplug
dumpsys battery unplug
heroltelgt:/ $ dumpsys deviceidle step
dumpsys deviceidle step
Stepped to deep: IDLE_PENDING
heroltelgt:/ $ dumpsys deviceidle step
dumpsys deviceidle step
Stepped to deep: SENSING
heroltelgt:/ $ dumpsys deviceidle step
dumpsys deviceidle step
Stepped to deep: LOCATING
heroltelgt:/ $ dumpsys deviceidle step
dumpsys deviceidle step
Stepped to deep: IDLE
heroltelgt:/ $ dumpsys deviceidle step
dumpsys deviceidle step
Stepped to deep: IDLE_MAINTENANCE
heroltelgt:/ $ dumpsys deviceidle step
dumpsys deviceidle step
Stepped to deep: IDLE

10-29 18:28:50.598 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:29:50.678 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:30:50.735 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:31:50.797 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:32:50.853 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 18:33:50.938 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
=> 손으로 눌러서 깨어남
10-29 18:36:24.202 18166-18166/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm

잠자기 모드에서는 알람이 울리지 않고 깨어 났을때 다시 알람이 울린다.

앱대기 모드

https://developer.android.com/training/monitoring-device-state/doze-standby.html#whitelisting-cases
앱대기 모드는 위 링크에서 나온 대로 하면 잘 동작하지 않습니다.
아래 링크의 앱 절전과 앱 대기를 같이 사용해보니 적절히 동작되었습니다.
https://github.com/gotev/android-upload-service/issues/227

    $ adb shell dumpsys deviceidle enable
    $ adb shell dumpsys deviceidle force-idle

    $ adb shell dumpsys battery unplug
    $ adb shell am set-inactive <packageName> true

heroltelgt:/ $ dumpsys deviceidle enable
dumpsys deviceidle enable
heroltelgt:/ $ dumpsys deviceidle force-idle
dumpsys deviceidle force-idle
Now forced in to deep idle mode
heroltelgt:/ $ dumpsys battery unplug
dumpsys battery unplug
heroltelgt:/ $ am get-inactive com.darts.test.alarmfordozemode.alarmfordozemode
ctive com.darts.test.alarmfordozemode.alarmfordozemode                        <
Idle=false
heroltelgt:/ $ am set-inactive com.darts.test.alarmfordozemode.alarmfordozemode true
ctive com.darts.test.alarmfordozemode.alarmfordozemode true                   <
heroltelgt:/ $ am get-inactive com.darts.test.alarmfordozemode.alarmfordozemode
ctive com.darts.test.alarmfordozemode.alarmfordozemode                        <
Idle=true
heroltelgt:/ $ am get-inactive com.darts.test.alarmfordozemode.alarmfordozemode
ctive com.darts.test.alarmfordozemode.alarmfordozemode                        <
Idle=true
heroltelgt:/ $ am get-inactive com.darts.test.alarmfordozemode.alarmfordozemode
ctive com.darts.test.alarmfordozemode.alarmfordozemode                        <
Idle=true

get 을 호출했을때 Idle=true가 계속 나와야지 standby 모드 상태 진입된것

setExactAndAllowWhileIdle 사용, TIME 1분

10-29 19:43:03.127 8644-8644/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 19:52:02.840 8644-8644/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 19:53:02.888 8644-8644/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 20:02:02.907 8644-8644/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 20:11:02.891 8644-8644/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
시간이 불규칙하고 안깨어 나는 경우도 있음

setExactAndAllowWhileIdle 사용, TIME 10분

10-29 19:23:15.062 18777-18777/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
10-29 19:33:15.145 18777-18777/com.darts.test.alarmfordozemode.alarmfordozemode E/ATest: Alarm
잘동작됨




댓글 없음:

댓글 쓰기