Android Doze模式源码分析,androiddoze
科技的仿生学无处不在,给予我们启发。为了延长电池是使用寿命,google从蛇的冬眠中得到体会,那就是在某种情况下也让手机进入类冬眠的情况,从而引入了今天的主题,Doze模式,Doze中文是打盹儿,打盹当然比活动节约能量了。
手机打盹儿的时候会怎样呢?
按照google的官方说法,Walklocks,网络访问,jobshedule,闹钟,GPS/WiFi扫描都会停止。这些停止后,将会节省30%的电量。
手机什么时候才会开始打盹呢?
上图是谷歌的Doze时序示意图,可以看出让手机打盹要满足三个条件
1.屏幕熄灭
2 .不插电
3.静止不动
这个是不是很仿生学呢?屏幕熄灭->闭上双眼,不插电->不吃东西,静止不动->安静地做个睡美人。生物不也是要满足这些条件才能打盹吗?妙,是在妙!
打盹总得呼吸吧?上图中的maintenance window就是给你呼吸的!!呼吸的时候Walklocks,网络访问,jobshedule,闹钟,GPS/WiFi扫描这些都会恢复,来吧重重的吸一口新鲜空气吧!随着时间的推移,呼吸的间隔会越变越大,而每次呼吸的时间也会变长,当然,伙计,不会无限长!!最后都会归于一个定值。下面分析源码就知道了,biu!
没源码,说个球儿
下面以一台手机静静地放在桌面上,随着时间的推移,进入doze模式的过程来分析源码。
源码路径:/frameworks/base/services/core/java/com/android/server/DeviceIdleController.java
系统中用一个全局整形变量来表示当前doze的状态
1 private int mState;
状态值的可能取值有以下,一开始的状态是STATE_ACTIVE。会依次经过1,2,3,4,状态后进入5状态,即STATE_IDLE
1 private static final int STATE_ACTIVE = 0; 2 private static final int STATE_INACTIVE = 1; 3 private static final int STATE_IDLE_PENDING = 2; 4 private static final int STATE_SENSING = 3; 5 private static final int STATE_LOCATING = 4; 6 private static final int STATE_IDLE = 5; 7 private static final int STATE_IDLE_MAINTENANCE = 6;
首先屏幕熄灭,回调熄屏处理函数
1 private final DisplayManager.DisplayListener mDisplayListener 2 = new DisplayManager.DisplayListener() { 3 @Override public void onDisplayAdded(int displayId) { 4 } 5 6 @Override public void onDisplayRemoved(int displayId) { 7 } 8 9 @Override public void onDisplayChanged(int displayId) { 10 if (displayId == Display.DEFAULT_DISPLAY) { 11 synchronized (DeviceIdleController.this) { 12 updateDisplayLocked(); //屏幕状态改变 13 } 14 } 15 } 16 };
进入updateDisplayLocked
1 void updateDisplayLocked() { 2 ... 3 becomeInactiveIfAppropriateLocked(); //看是否可以进入Inactive状态 4 .... 5 } 6 }
然后我们拔出usb,不充电,会回调充电处理函数
1 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 2 @Override public void onReceive(Context context, Intent intent) { 3 if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { 4 int plugged = intent.getIntExtra("plugged", 0); 5 updateChargingLocked(plugged != 0); //充电状态改变 6 } else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) { 7 synchronized (DeviceIdleController.this) { 8 stepIdleStateLocked(); 9 } 10 } 11 } 12 };
进入updateChargingLocked
1 void updateChargingLocked(boolean charging) { 2 .... 3 becomeInactiveIfAppropriateLocked();//看是否可以进入Inactive状态 4 ..... 5 }
最后不插电和熄灭屏幕后都会进入becomeInactiveIfAppropriateLocked,状态mState变成STATE_INACTIVE,并且开启了一个定时器
1 void becomeInactiveIfAppropriateLocked() { 2 if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()"); 3 //不插电和屏幕熄灭的条件都满足了 4 if (((!mScreenOn && !mCharging) || mForceIdle) && mEnabled && mState == STATE_ACTIVE) { 5 ..... 6 mState = STATE_INACTIVE; 7 scheduleAlarmLocked(mInactiveTimeout, false); 8 ...... 9 } 10 } 11 12 定时时长为常量30分钟 13 INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT, 14 !COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);
手机静静地躺在桌面上30分钟后,定时器时间到达后,pendingintent会被发出,广播接收器进行处理
1 Intent intent = new Intent(ACTION_STEP_IDLE_STATE) 2 .setPackage("android") 3 .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 4 mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0); 5 6 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 7 @Override public void onReceive(Context context, Intent intent) { 8 if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { 9 int plugged = intent.getIntExtra("plugged", 0); 10 updateChargingLocked(plugged != 0);