Android Doze模式源碼分析,androiddoze

來源:互聯網
上載者:User

Android Doze模式源碼分析,androiddoze

 

科技的仿生學無處不在,給予我們啟發。為了延長電池是使用壽命,google從蛇的冬眠中得到體會,那就是在某種情況下也讓手機進入類冬眠的情況,從而引入了今天的主題,Doze模式,Doze中文是打盹兒,打盹當然比活動節約能量了。

手機打盹兒的時候會怎樣呢?

按照google的官方說法,Walklocks,網路訪問,jobshedule,鬧鐘,GPS/WiFi掃描都會停止。這些停止後,將會節省30%的電量。

手機什麼時候才會開始打盹呢?

是Google的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);11             } else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) {12                 synchronized (DeviceIdleController.this) {13                     stepIdleStateLocked();   //接收到廣播14                 }15             }16         }17     };

進入stepIdleStateLocked,該函數是狀態轉換處理的主要函數

 1     void stepIdleStateLocked() { 2         if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState); 3         EventLogTags.writeDeviceIdleStep(); 4  5         final long now = SystemClock.elapsedRealtime(); 6         if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) { 7             // Whoops, there is an upcoming alarm.  We don't actually want to go idle. 8             if (mState != STATE_ACTIVE) { 9                 becomeActiveLocked("alarm", Process.myUid());10             }11             return;12         }13 14         switch (mState) {15             case STATE_INACTIVE:16                 // We have now been inactive long enough, it is time to start looking17                 // for significant motion and sleep some more while doing so.18                 startMonitoringSignificantMotion(); //觀察是否有小動作19                 scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false); //設定觀察小動作要觀察多久20                 mState = STATE_IDLE_PENDING; //狀態更新為STATE_IDLE_PENDING21                 break;22             case STATE_IDLE_PENDING: //小動作觀察結束,很厲害,一直都沒有小動作,會進入這裡23                 mState = STATE_SENSING;//狀態更新為STATE_SENSING24                 scheduleSensingAlarmLocked(mConstants.SENSING_TIMEOUT);//設定感應器感應時間長度25                 mAnyMotionDetector.checkForAnyMotion(); //感應器感應手機有沒有動26                 break;27             case STATE_SENSING: //感應器也沒發現手機動,就來最後一發,看GPS有沒有動28                 mState = STATE_LOCATING;//狀態更新為STATE_LOCATING29                 scheduleSensingAlarmLocked(mConstants.LOCATING_TIMEOUT);//設定GPS觀察時間長度30                 mLocationManager.requestLocationUpdates(mLocationRequest, mGenericLocationListener,31                         mHandler.getLooper());//GPS開始感應32                 break;33             case STATE_LOCATING:  //GPS也發現沒動34                 cancelSensingAlarmLocked();35                 cancelLocatingLocked();36                 mAnyMotionDetector.stop();  //這裡沒有break,直接進入下一個case37             case STATE_IDLE_MAINTENANCE:38                 scheduleAlarmLocked(mNextIdleDelay, true);//設定打盹多久後進行呼吸39                 mNextIdleDelay = (long)(mNextIdleDelay * mConstants.IDLE_FACTOR);//更新下次打盹多久後進行呼吸40                 if (DEBUG) Slog.d(TAG, "Setting mNextIdleDelay = " + mNextIdleDelay);41                 mNextIdleDelay = Math.min(mNextIdleDelay, mConstants.MAX_IDLE_TIMEOUT);42                 mState = STATE_IDLE; //噢耶 終於進入了STATE_IDLE43                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON);44                 break;45             case STATE_IDLE: //打盹完了,呼吸一下就是這裡了46                 scheduleAlarmLocked(mNextIdlePendingDelay, false);47                 mState = STATE_IDLE_MAINTENANCE; //狀態更新為STATE_IDLE_MAINTENANCE48                 mNextIdlePendingDelay = Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,49                         (long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));50                //更新下次呼吸的時間51                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);52                 break;53         }54     }

Math.min(mConstants.MAX_IDLE_PENDING_TIMEOUT,
(long)(mNextIdlePendingDelay * mConstants.IDLE_PENDING_FACTOR));
這一句看到了嗎?取最小值,這裡就是保證了idle和視窗的時間不會變成無限大。
為了讓各位有個感官的體驗,上面的一些時間我直接列出來吧

熄屏不插電進入INACTIVE時間上面說了30分鐘觀察小動作的時間30分鐘IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT,                        !COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);觀察感應器的時間4分鐘SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,                        !DEBUG ? 4 * 60 * 1000L : 60 * 1000L);觀察GPS的時間30秒LOCATING_TIMEOUT = mParser.getLong(KEY_LOCATING_TIMEOUT,                        !DEBUG ? 30 * 1000L : 15 * 1000L);

所以進入idle的總時間為30分鐘+30分鐘+4分鐘+30s=1小時4分鐘30秒,哈哈哈哈!!

下面給張狀態轉換圖看看,沒到達idle狀態前,基本上有什麼風吹草動都會變回ACTIVE狀態。而變成IDLE狀態後,只能插電或者點亮螢幕才離開IDLE狀態。就像人入睡前,很容易被吵醒,而深度入眠後,估計只有鬧鐘能鬧醒你了!!

 

 

上面說了這麼多,跟我應用開發有什麼關係?

其實,沒多大關係,看下源碼不行噻。
不過作為一種新的機制,最好測試下你的應用在這幾種狀態下是否能夠正常運行,起碼不能掛掉啊。
google提供了adb的指令來強制變換狀態,這樣你就不用乾等著它狀態變化了。

1 adb shell dumpsys battery unplug       //相當於不插電2 adb shell dumpsys device idle step     //讓狀態轉換

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.