Android中通過NTP伺服器獲取時間功能原始碼分析
阿新 • • 發佈:2019-02-02
1 相關檔案:
frameworks\base\services\java\com\android\server\ SystemServer.java
frameworks\base\services\java\com\android\server\ NetworkTimeUpdateService.java
frameworks\base\core\java\android\util\NtpTrustedTime.java
frameworks\base\core\java\android\net\SntpClient.java
frameworks\base\core\res\res\values\config.xml
2 實現原理:
2.1 SystemServer的run中:
ActivityManagerService.self().systemReady(new Runnable() {
public void run() {
try {
if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Time Service ready", e);
}
...
}
2.2 再來看看NetworkTimeUpdateService中的相關程式碼:
systemReady
MyHandler
在看這個函式之前先來理解幾個相關變數,理解了這幾個變數之後,該函式就比較好理解了。
在NetworkTimeUpdateService的建構函式中:
幾個關鍵的變數:
mPollingIntervalMs
當NTP時間獲取成功後,再次請求NTP時間的間隔
mPollingIntervalShorterMs
當NTP時間獲取失敗後,再次請求NTP時間的間隔
mTimeErrorThresholdMs
當NTP時間和系統時間不相同時,要更新系統時間的閥值
這幾個變數的值是通過資原始檔裡讀取的,配置的地址為config.xml,來看看相關的內容:
frameworks\base\services\java\com\android\server\ SystemServer.java
frameworks\base\services\java\com\android\server\ NetworkTimeUpdateService.java
frameworks\base\core\java\android\util\NtpTrustedTime.java
frameworks\base\core\java\android\net\SntpClient.java
frameworks\base\core\res\res\values\config.xml
2 實現原理:
2.1 SystemServer的run中:
ActivityManagerService.self().systemReady(new Runnable() {
public void run() {
try {
if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady();
} catch (Throwable e) {
reportWtf("making Network Time Service ready", e);
}
...
}
2.2 再來看看NetworkTimeUpdateService中的相關程式碼:
systemReady
public void systemReady() { registerForTelephonyIntents(); //註冊定時器廣播 registerForAlarms(); //註冊網路連線訊息廣播 registerForConnectivityIntents(); //建立用於接收NTP請求事件的HandlerThread //用於處理: // EVENT_AUTO_TIME_CHANGED: // EVENT_POLL_NETWORK_TIME: // EVENT_NETWORK_CONNECTED: //三個訊息 mThread = new HandlerThread(TAG); mThread.start(); mHandler = new MyHandler(mThread.getLooper()); // Check the network time on the new thread //傳送請求NTP時間訊息 mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget(); //新增一個用於監聽設定中時間改變訊息通知 mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED); mSettingsObserver.observe(mContext); }
MyHandler
/** Handler to do the network accesses on */ private class MyHandler extends Handler { public MyHandler(Looper l) { super(l); } @Override public void handleMessage(Message msg) { switch (msg.what) { case EVENT_AUTO_TIME_CHANGED: case EVENT_POLL_NETWORK_TIME: case EVENT_NETWORK_CONNECTED: onPollNetworkTime(msg.what); break; } } }
重點來看看onPollNetworkTime這個函式:
在看這個函式之前先來理解幾個相關變數,理解了這幾個變數之後,該函式就比較好理解了。
在NetworkTimeUpdateService的建構函式中:
public NetworkTimeUpdateService(Context context) { mContext = context; mTime = NtpTrustedTime.getInstance(context); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); Intent pollIntent = new Intent(ACTION_POLL, null); mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0); mPollingIntervalMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpPollingInterval); mPollingIntervalShorterMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpPollingIntervalShorter); mTryAgainTimesMax = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpRetry); mTimeErrorThresholdMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpThreshold); }
幾個關鍵的變數:
mPollingIntervalMs
當NTP時間獲取成功後,再次請求NTP時間的間隔
mPollingIntervalShorterMs
當NTP時間獲取失敗後,再次請求NTP時間的間隔
mTimeErrorThresholdMs
當NTP時間和系統時間不相同時,要更新系統時間的閥值
這幾個變數的值是通過資原始檔裡讀取的,配置的地址為config.xml,來看看相關的內容:
0.android.pool.ntp.org8640000005000-1500020000
其實只要看下注釋這幾個變數的功能就清楚了,可見註釋是多麼的重要,如果要自己看程式碼理解的話,可能要花比較多的時間。
好,最後來看下onPollNetworkTime的程式碼: private void onPollNetworkTime(int event) {
// If Automatic time is not set, don't bother.
if (!isAutomaticTimeRequested()) return;
final long refTime = SystemClock.elapsedRealtime();
// If NITZ time was received less than mPollingIntervalMs time ago,
// no need to sync to NTP.
if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
resetAlarm(mPollingIntervalMs);
return;
}
final long currentTime = System.currentTimeMillis();
if (DBG) Log.d(TAG, "System time = " + currentTime);
// Get the NTP time
if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
|| event == EVENT_AUTO_TIME_CHANGED) {
if (DBG) Log.d(TAG, "Before Ntp fetch");
// force refresh NTP cache when outdated
//如果沒有獲取過NTP時間或者系統時間距離最後一次獲取NTP時間超過了mPollingIntervalMs,就去請求NTP時間
if (mTime.getCacheAge() >= mPollingIntervalMs) {
mTime.forceRefresh();
}
// only update when NTP time is fresh
if (mTime.getCacheAge() < mPollingIntervalMs) {
final long ntp = mTime.currentTimeMillis();
mTryAgainCounter = 0;
// If the clock is more than N seconds off or this is the first time it's been
// fetched since boot, set the current time.
if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
|| mLastNtpFetchTime == NOT_SET) {
// Set the system time
if (DBG && mLastNtpFetchTime == NOT_SET
&& Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
Log.d(TAG, "For initial setup, rtc = " + currentTime);
}
if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
// Make sure we don't overflow, since it's going to be converted to an int
if (ntp / 1000 < Integer.MAX_VALUE) {
SystemClock.setCurrentTimeMillis(ntp);
}
} else {
if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
}
mLastNtpFetchTime = SystemClock.elapsedRealtime();
} else {
// Try again shortly
mTryAgainCounter++;
if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
resetAlarm(mPollingIntervalShorterMs);
} else {
// Try much later
mTryAgainCounter = 0;
resetAlarm(mPollingIntervalMs);
}
return;
}
}
resetAlarm(mPollingIntervalMs);
}
改了下,個人感覺比原來程式碼更容易理解了:
private void onPollNetworkTime(int event) {
// If Automatic time is not set, don't bother.
if (!isAutomaticTimeRequested()) return;
final long refTime = SystemClock.elapsedRealtime();
// If NITZ time was received less than mPollingIntervalMs time ago,
// no need to sync to NTP.
if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < mPollingIntervalMs) {
resetAlarm(mPollingIntervalMs);
return;
}
final long currentTime = System.currentTimeMillis();
if (DBG) Log.d(TAG, "System time = " + currentTime);
// Get the NTP time
if (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs
|| event == EVENT_AUTO_TIME_CHANGED) {
if (DBG) Log.d(TAG, "Before Ntp fetch");
if (mTime.getCacheAge() >= mPollingIntervalMs && !mTime.forceRefresh()) {
mTryAgainCounter++;
if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
resetAlarm(mPollingIntervalShorterMs);
} else {
// Try much later
mTryAgainCounter = 0;
resetAlarm(mPollingIntervalMs);
}
return;
}
final long ntp = mTime.currentTimeMillis();
mTryAgainCounter = 0;
// If the clock is more than N seconds off or this is the first time it's been
// fetched since boot, set the current time.
if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs
|| mLastNtpFetchTime == NOT_SET) {
// Set the system time
if (DBG && mLastNtpFetchTime == NOT_SET
&& Math.abs(ntp - currentTime) <= mTimeErrorThresholdMs) {
Log.d(TAG, "For initial setup, rtc = " + currentTime);
}
if (DBG) Log.d(TAG, "Ntp time to be set = " + ntp);
// Make sure we don't overflow, since it's going to be converted to an int
if (ntp / 1000 < Integer.MAX_VALUE) {
SystemClock.setCurrentTimeMillis(ntp);
}
} else {
if (DBG) Log.d(TAG, "Ntp time is close enough = " + ntp);
}
mLastNtpFetchTime = SystemClock.elapsedRealtime();
resetAlarm(mPollingIntervalMs);
}
哪個程式碼更清晰,大家仁者見仁,智者見智,各取所好。
當然,我要宣告一下,雖然我很有信心我改的程式碼沒有問題,但是該程式碼我沒有經過測試的,所以不要隨便替換。
多年的寫程式碼的經驗告訴我,自信要有,但是不要自負,大腦是的優勢在於創造,而機器的優勢在於精確。
所以,在實際的工作中,寫完程式碼之後,寫測試用例測試下吧!
(完)