android 设置时间自动更新,Android中的時間自動更新

最近幾天,一直糾結於android的時間的自動更新,先簡要說下android自己原有的更新機制,android原有的更新機制很簡單,是采用NITZ(Network identity and Time Zone)的方式更新的,這應該是一種運營商的可選服務,簡單的來說,就是運營商通知CP主動上報時間信息,CP上報后上層更新相應的時間。CDMA制式估計上報時間比較頻繁,更新比較給力,因此CDMA制式的時間只能自動更新,而不會讓用戶手動設置的(原因僅僅為個人猜測)。WCDMA和GSM的就比較悲催了,如果你等主動上報的更新消息,可能得等N長時間還是不能更新。

但是,也不是說沒有辦法讓時間自動更新,采用SNTP方式的話,我們也能自動更新時間,但是,采用SNTP的話,是不能更新時區的。

下面以4.0的代碼為例(2.3的基本類似且更簡單,並且2.3中沒有自帶SNTP的更新方式)來分析下android的自動更新時間原理。

1.選中自動更新

其實就是在數據庫中設置了一個值,2.3中只有一個選項-同步,就是會同步時區和時間日期,4.0中把他們分成了兩項,時區和日期時間能分別進行自動更新,其實原理都是一樣,都是在數據庫中設置了一個值。

代碼路徑:packages/apps/Settings/src/com/android/settings/DateTimeSettings.java

@Override

public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {

if (key.equals(KEY_DATE_FORMAT)) {

String format = preferences.getString(key,

getResources().getString(R.string.default_date_format));

Settings.System.putString(getContentResolver(),

Settings.System.DATE_FORMAT, format);

updateTimeAndDateDisplay(getActivity());

} else if (key.equals(KEY_AUTO_TIME)) {

boolean autoEnabled = preferences.getBoolean(key, true);

Settings.System.putInt(getContentResolver(), Settings.System.AUTO_TIME,

autoEnabled ? 1 : 0);

mTimePref.setEnabled(!autoEnabled);

mDatePref.setEnabled(!autoEnabled);

} else if (key.equals(KEY_AUTO_TIME_ZONE)) {

boolean autoZoneEnabled = preferences.getBoolean(key, true);

Settings.System.putInt(

getContentResolver(), Settings.System.AUTO_TIME_ZONE, autoZoneEnabled ? 1 : 0);

mTimeZone.setEnabled(!autoZoneEnabled);

}

}

KEY_AUTO_TIME就是更新時間的CheckBoxPreference,下面的KEY_AUTO_TIME_ZONE就是更新時區的KEY_AUTO_TIME_ZONE,這兩個操作僅僅只是設置了兩個值下去而已,那么真正干事情的地方在哪兒呢?

2.真正做事的地方

我們選中CheckBoxPreference,僅僅只是更改了數據庫的值,但是時間和時區還是得更新的,那么更新它們的地方在哪兒呢?在GsmServiceStateTracker.java中。

代碼路徑為framworks/base/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java

里面有兩個ContentObserver來監聽數據庫內容的變化,讓我們來看下這兩個ContentObserver的定義。

cr = phone.getContext().getContentResolver();

cr.registerContentObserver(

Settings.System.getUriFor(Settings.System.AUTO_TIME), true,

mAutoTimeObserver);

cr.registerContentObserver(

Settings.System.getUriFor(Settings.System.AUTO_TIME_ZONE), true,

mAutoTimeZoneObserver);

可以很明顯的看到兩個監聽的和我們設置的URI是一樣的。那我們再來看下這兩個ContentObserver干了啥事

private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {

@Override

public void onChange(boolean selfChange) {

Log.i("GsmServiceStateTracker", "Auto time state changed");

revertToNitzTime();

}

};

private ContentObserver mAutoTimeZoneObserver = new ContentObserver(new Handler()) {

@Override

public void onChange(boolean selfChange) {

Log.i("GsmServiceStateTracker", "Auto time zone state changed");

revertToNitzTimeZone();

}

};

OK,真相出來了,正是他們更新了時間和時區。好的,下一步,我們繼續分析時間和時區究竟是如何更新的。

3.更新時間和時區

先來分析更新時間。可能大家用過4.0的非CDMA制式手機就會有這樣的感覺,選擇自動更新時間,很快就自動更新了,選擇自動更新時區,發現有時候過了很久很久都沒有更新。OK,讓我們先來分析下,時間是怎么自動更新的。

來分析下revertToNitzTime()函數。

private void revertToNitzTime() {

if (Settings.System.getInt(phone.getContext().getContentResolver(),

Settings.System.AUTO_TIME, 0) == 0) {

return;

}

if (DBG) {

log("Reverting to NITZ Time: mSavedTime=" + mSavedTime

+ " mSavedAtTime=" + mSavedAtTime);

}

if (mSavedTime != 0 && mSavedAtTime != 0) {

setAndBroadcastNetworkSetTime(mSavedTime

+ (SystemClock.elapsedRealtime() - mSavedAtTime));

}

}

這個函數做的事情好簡單啊,就是判斷了下有沒有選中自動更新啊,沒有,那么返回。有,那再繼續判斷,mSavedTime和mSavedAtTime為不為0啊(這兩個變量后面再講),都不為0,那么就要發廣播了。

private void setAndBroadcastNetworkSetTime(long time) {

SystemClock.setCurrentTimeMillis(time);

Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);

intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);

intent.putExtra("time", time);

phone.getContext().sendStickyBroadcast(intent);

}

發送廣播如上,找到廣播接收的地方NetworkTimeUpdateService.java

路徑如下:frameworks/base/services/java/com/android/server/NetworkTimeUpdateService.java

找到它的receiver,驚訝的發現,什么事都沒干,就是賦值了兩個變量。

@Override

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

if (TelephonyIntents.ACTION_NETWORK_SET_TIME.equals(action)) {

mNitzTimeSetTime = SystemClock.elapsedRealtime();

} else if (TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE.equals(action)) {

mNitzZoneSetTime = SystemClock.elapsedRealtime();

}

}

真正更新時間的地方在哪兒?

答案還是在NetworkTimeupdateService.java中,它也注冊了ContentObserver。

mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);

mSettingsObserver.observe(mContext);

SettingsObserver就是一個ContentObserver,具體的代碼我就不貼出來了,很簡單,大家可以自己去看。

好的,繼續分析更改時間的地方,找到handleMessage里的回調函數,onPollNetworkTime(),這個函數很長,我就簡單的貼出部分關鍵代碼

// If NITZ time was received less than POLLING_INTERVAL_MS time ago,

// no need to sync to NTP.

if (mNitzTimeSetTime != NOT_SET && refTime - mNitzTimeSetTime < POLLING_INTERVAL_MS) {

resetAlarm(POLLING_INTERVAL_MS);

return;

}

.......

if (mTime.getCacheAge() < POLLING_INTERVAL_MS) {

final long ntp = mTime.currentTimeMillis();

.....

}

OK,看注釋,看到我們更新的NITZ時間不為NOT_SET(-1),且更新的時間小於POLLING_INTERVAL_MS,那么就直接更新NITZ的時間,否則用SNTP更新時間。那么,NITZ的時間是從何而來的呢,我們進一步分析。

4.為什么不能更新時區,卻能更新時間

看看NITZ的時間究竟從何而來,找到廣播的發送處。GsmServiceStateTracker中,我們發送的是mSavedTime

+ (SystemClock.elapsedRealtime() - mSavedAtTime)。

看看這兩個變量在哪里賦值,在setTimeFromNITZString()中,那么我們往上追蹤就會發現,這個函數是由RIL_UNSOL_NITZ_TIME_RECEIVED這個主動上報的消息激發的,至此,我們終於找到了時間更新原理。先看有沒有RIL的主動上報,如果有,那么就用這種主動上報的NITZ時間更新,如果沒有,那么就選擇用SNTP更新時間。

那么,時區為什么不能自動更新呢,那是因為,如果沒有RIL的主動上報,時區就沒有了初始值的,而SNTP不能更新時區。所以,時區只能用NITZ更新。

我們來用代碼驗證下。

先來看發廣播的地方,廣播仍然發到了NetworkTimeUpdateService,它是更改了mNitzZoneSetTime,但是,我們驚奇的發現,這個變量值一點作用都沒有,而且在mNitzZoneSetTime中,我們也沒有自動更新時區的監聽。因此,時區完全被我們拋棄了。。。

那在GsmServiceStateTracker中呢,看看RIL主動上報干了些啥事

String iso = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY);

if (zone == null) {

if (mGotCountryCode) {

if (iso != null && iso.length() > 0) {

zone = TimeUtils.getTimeZone(tzOffset, dst != 0,

c.getTimeInMillis(),

iso);

} else {

// We don't have a valid iso country code. This is

// most likely because we're on a test network that's

// using a bogus MCC (eg, "001"), so get a TimeZone

// based only on the NITZ parameters.

zone = getNitzTimeZone(tzOffset, (dst != 0), c.getTimeInMillis());

}

}

}

if (zone == null) {

// We got the time before the country, so we don't know

// how to identify the DST rules yet. Save the information

// and hope to fix it up later.

mNeedFixZone = true;

mZoneOffset = tzOffset;

mZoneDst = dst != 0;

mZoneTime = c.getTimeInMillis();

}

if (zone != null) {

if (getAutoTimeZone()) {

setAndBroadcastNetworkSetTimeZone(zone.getID());

}

saveNitzTimeZone(zone.getID());

}

利用得到的國家碼和偏移值算出時區,而這個算出來的時區會保存在變量中,當你選擇自動更新的時候,會把這個變量賦值給上層完成更新。好了,大體上更新時間和時區就是這樣,還有部分是天線狀態改變的時候會觸發時間和時區的更改,這里就不討論了,大家有興趣的可以看下,代碼同樣在GsmServiceStateTracker中。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值