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
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
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,来看看相关的内容:
translatable="false"
name="config_ntpServer">0.android.pool.ntp.org
name="config_ntpPollingInterval">864000000
name="config_ntpPollingIntervalShorter">5000
name="config_ntpRetry">-1
name="config_ntpThreshold">5000
name="config_ntpTimeout">20000
其实只要看下注释这几个变量的功能就清楚了,可见注释是多么的重要,如果要自己看代码理解的话,可能要花比较多的时间。
好,最后来看下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 <</span> 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() <</span> 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 <</span>
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 <</span> 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 <</span> 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 <</span> 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 <</span>
Integer.MAX_VALUE) {
SystemClock.setCurrentTimeMillis(ntp);
}
} else {
if (DBG)
Log.d(TAG, "Ntp time is
close enough = " + ntp);
}
mLastNtpFetchTime = SystemClock.elapsedRealtime();
resetAlarm(mPollingIntervalMs);
}
哪个代码更清晰,大家仁者见仁,智者见智,各取所好。
当然,我要声明一下,虽然我很有信心我改的代码没有问题,但是该代码我没有经过测试的,所以不要随便替换。
多年的写代码的经验告诉我,自信要有,但是不要自负,大脑是的优势在于创造,而机器的优势在于精确。
所以,在实际的工作中,写完代码之后,写测试用例测试下吧!
(完)
加载中,请稍候......