转载请注明出处:http://blog.csdn.net/droyon/article/details/45701257

综述:Android网络时间更新,大体分两类。1、moderm相关更新,2、网络更新。本次主要介绍网路更新时间,主要涉及到NetworkTimeUpdateService,该类运行在SystemServer(ActivityManagerService)进程中。它有点特殊,从名字来看,其实Service,其实它和WifiService、ConnectivityManagerService等系统Service不同。
SystemServer.java

try {startBootstrapServices();startCoreServices();startOtherServices();} catch (Throwable ex) {Slog.e("System", "******************************************");Slog.e("System", "************ Failure starting system services", ex);throw ex;}

startOtherServices方法中,会初始化该类实例:

networkTimeUpdater = new NetworkTimeUpdateService(context);

在ActivityManagerService的systemReady方法中,初始化时间更新环境。

mActivityManagerService.systemReady(new Runnable() {@Overridepublic void run() {try {if (networkManagementF != null)       networkManagementF.systemReady();} catch (Throwable e) {reportWtf("making Network Managment Service ready", e);}}
}

涉及代码路径如下:
frameworks/base/services/core/java/com/android/server/NetworkTimeUpdateService.java
frameworks/base/core/java/android/util/NtpTrustedTime.java
frameworks/base/core/java/android/net/SntpClient.java

一、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);//时间同步有可能超时,使用该PendingIntent进行(间隔再次发起)时间同步。mPollingIntervalMs = mContext.getResources().getInteger(com.android.internal.R.integer.config_ntpPollingInterval);//10天mPollingIntervalShorterMs = mContext.getResources().getInteger(com.android.internal.R.integer.config_ntpPollingIntervalShorter);//30秒mTryAgainTimesMax = mContext.getResources().getInteger(com.android.internal.R.integer.config_ntpRetry);mTimeErrorThresholdMs = mContext.getResources().getInteger(com.android.internal.R.integer.config_ntpThreshold);mDefaultServer = ((NtpTrustedTime) mTime).getServer();mNtpServers.add(mDefaultServer);for (String str : SERVERLIST){mNtpServers.add(str);}mTryAgainCounter = 0;}

在该构造上,有几个重要的变量:
1、mPollingIntervalMs:多次尝试同步时间无果,10天会再次发起时间同步请求
2、mPollingIntervalShorterMs :时间同步超时,再次发起时间同步请求。
3、SERVERLIST:时间同步服务器。此处建议多增加几个时间同步服务器,大陆、美国、台湾等多梯度配置。
4、初始化NtpTrustedTime对象。

mTime = NtpTrustedTime.getInstance(context);

一、NetworkTimeUpdateService初始化时间同步环境
开机后,会调用该类的systemRunning方法,在该方法中:

public void systemRunning() {registerForTelephonyIntents();registerForAlarms();registerForConnectivityIntents();HandlerThread thread = new HandlerThread(TAG);thread.start();mHandler = new MyHandler(thread.getLooper());// Check the network time on the new threadmHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);mSettingsObserver.observe(mContext);}

1、registerForTelephonyIntents该方法,注册监听来自Telephony Ril相关的广播。此部分会在moderm相关同步时间中介绍。

private void registerForTelephonyIntents() {IntentFilter intentFilter = new IntentFilter();intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIME);intentFilter.addAction(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);mContext.registerReceiver(mNitzReceiver, intentFilter);}

2、registerForAlarms此方法,是配合第“一”中介绍的mPendingPollIntent 来工作的,主要作用是构造handler Message并再次发起时间同步请求。
3、registerForConnectivityIntents此方法监听移动数据连接,移动网络连接后,收到信息,发起时间同步请求。此部分会在moderm相关同步时间中介绍。

private void registerForConnectivityIntents() {IntentFilter intentFilter = new IntentFilter();intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);mContext.registerReceiver(mConnectivityReceiver, intentFilter);}

4、构建Message,发起时间同步请求。

HandlerThread thread = new HandlerThread(TAG);thread.start();mHandler = new MyHandler(thread.getLooper());// Check the network time on the new threadmHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();

5、构建监听数据库的Observer,监听来自设置等发起的时间同步请求。在SettingsObserver中构建handler Message请求,发起时间同步。

mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);mSettingsObserver.observe(mContext);

我们的第二部分,很多地方都会主动或者被动发送Handler Message请求,在我们Handler中,我们是如何处理的那?

三、时间同步请求处理逻辑。
在第二部分,我们讲到了接收的来自Telephony相关的广播,或者数据库变化,我们都会发送Message给Handler,我们的handler是如下处理这些请求的:

private class MyHandler extends Handler {public MyHandler(Looper l) {super(l);}@Overridepublic 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;}}}

接收请求类型:EVENT_AUTO_TIME_CHANGED、EVENT_POLL_NETWORK_TIME、
EVENT_NETWORK_CONNECTED,这些请求逻辑,我们都会发起onPollNetworkTime来进行相关逻辑处理。
也就是说,onPollNetworkTime方法就是我们时间同步的主要关注对象。
1、onPollNetworkTime:

private void onPollNetworkTime(int event) {
//1、是否勾选自动同步时间配置// If Automatic time is not set, don't bother.if (!isAutomaticTimeRequested()) return;
//2、mNitzTimeSetTime 来自Moderm,如果当前时间刚通过moderm更新不久,则不进行时间同步。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;}//3、如果机器刚启动,或者机器运行时间大于mPollingIntervalMs,即10天,或者设置等发起的主动更新时间请求,则发起网络时间同步请求。否则,10天后再进行时间同步。final long currentTime = System.currentTimeMillis();if (DBG) Log.d(TAG, "System time = " + currentTime);// Get the NTP timeif (mLastNtpFetchTime == NOT_SET || refTime >= mLastNtpFetchTime + mPollingIntervalMs|| event == EVENT_AUTO_TIME_CHANGED) {if (DBG) Log.d(TAG, "Before Ntp fetch");
//3.1、是否含有时间缓冲,如无,发起时间同步,// force refresh NTP cache when outdatedif (mTime.getCacheAge() >= mPollingIntervalMs) {//mTime.forceRefresh();int index = mTryAgainCounter % mNtpServers.size();if (DBG) Log.d(TAG, "mTryAgainCounter = " + mTryAgainCounter + ";mNtpServers.size() = " + mNtpServers.size() + ";index = " + index + ";mNtpServers = " + mNtpServers.get(index));//3.1.1、遍历时间服务器,发起时间同步if (mTime instanceof NtpTrustedTime){((NtpTrustedTime) mTime).setServer(mNtpServers.get(index));mTime.forceRefresh();((NtpTrustedTime) mTime).setServer(mDefaultServer);}else{mTime.forceRefresh();}}
//3.2、获取最新同步的时间缓冲数据,如无,则再次发起时间同步,间隔时间为mPollingIntervalShorterMs,即30秒。// only update when NTP time is freshif (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.//3.2.1、如果开机第一次同步或者最新时间与当前时间差别超过mTimeErrorThresholdMs即25秒,则进行时间设定。否则认定新同步时间与当前时间差别不大,不覆盖当前时间。if (Math.abs(ntp - currentTime) > mTimeErrorThresholdMs|| mLastNtpFetchTime == NOT_SET) {// Set the system timeif (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//3.2.2、设定同步时间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//3.3 如果不大于最大同步次数,30秒后进行时间同步,否则,10天后更新。mTryAgainCounter++;if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {resetAlarm(mPollingIntervalShorterMs);} else {// Try much latermTryAgainCounter = 0;resetAlarm(mPollingIntervalMs);}return;}}//4、如果刚更新时间不久,则10天后再发起时间同步请求。resetAlarm(mPollingIntervalMs);}

四、三中介绍了时间获取的相关逻辑,我们接下来介绍下时间是如何发起同步的,这个方法的主角为:NtpTrustedTime
在该类中通过forceRefresh方法来更新获取服务器时间。

public boolean forceRefresh() {if (mServer == null) {// missing server, so no trusted time availablereturn false;}if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");final SntpClient client = new SntpClient();if (client.requestTime(mServer, (int) mTimeout)) {mHasCache = true;mCachedNtpTime = client.getNtpTime();mCachedNtpElapsedRealtime = client.getNtpTimeReference();mCachedNtpCertainty = client.getRoundTripTime() / 2;return true;} else {return false;}}

在该方法逻辑中,通过SntpClient来封装请求。
SntpClient.java

public boolean requestTime(String host, int timeout) {DatagramSocket socket = null;try {socket = new DatagramSocket();socket.setSoTimeout(timeout);InetAddress address = InetAddress.getByName(host);byte[] buffer = new byte[NTP_PACKET_SIZE];DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT);// set mode = 3 (client) and version = 3// mode is in low 3 bits of first byte// version is in bits 3-5 of first bytebuffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);// get current time and write it to the request packetlong requestTime = System.currentTimeMillis();long requestTicks = SystemClock.elapsedRealtime();writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);socket.send(request);// read the responseDatagramPacket response = new DatagramPacket(buffer, buffer.length);socket.receive(response);long responseTicks = SystemClock.elapsedRealtime();long responseTime = requestTime + (responseTicks - requestTicks);// extract the resultslong originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime);// receiveTime = originateTime + transit + skew// responseTime = transmitTime + transit - skew// clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2//             = ((originateTime + transit + skew - originateTime) +//                (transmitTime - (transmitTime + transit - skew)))/2//             = ((transit + skew) + (transmitTime - transmitTime - transit + skew))/2//             = (transit + skew - transit + skew)/2//             = (2 * skew)/2 = skewlong clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2;// if (false) Log.d(TAG, "round trip: " + roundTripTime + " ms");// if (false) Log.d(TAG, "clock offset: " + clockOffset + " ms");// save our results - use the times on this side of the network latency// (response rather than request time)mNtpTime = responseTime + clockOffset;mNtpTimeReference = responseTicks;mRoundTripTime = roundTripTime;} catch (Exception e) {if (false) Log.d(TAG, "request time failed: " + e);return false;} finally {if (socket != null) {socket.close();}}return true;}

我们传入在NetworkTimeUpdateService传入的服务器地址以及请求超时时间,向host服务器发起请求,并将相应结果按照编解码规则封装进二进制数组。

总结:NetworkTimeUpdateService时间同步,一旦发起成功的时间同步,时间数据会存在内存中,并根据当前机器运行时间来设定最新的时间。

Android 时间更新机制之网络更新时间相关推荐

  1. native react 更新机制_react-native热更新全方位讲解

    最近在研究热更新技术,看了网上各个大佬的博客,整体流程上总是卡壳.跳了几天坑,刚刚终于把简单的热更新流程跑通,现在也正在一边学习更新,一边整理资料,在此篇博客上记录操作流程,希望我的实践能帮助各位同行 ...

  2. Android 系统时间自动更新机制

    两种时间更新机制 NITZ NITZ(Network Identity and Time Zone,网络标识和时区),是一种用于自动配置本地的时间和日期的机制,同时也通过无线网向移动设备提供运营商信息 ...

  3. Android 系统时间自动更新机制--解决 “时间和日期不准确“

    两种时间更新机制 NITZ NITZ(Network Identity and Time Zone,网络标识和时区),是一种用于自动配置本地的时间和日期的机制,同时也通过无线网向移动设备提供运营商信息 ...

  4. Android系统时间更新机制

    系统设置–日期和时间–自动确定日期和时间:  智能机 提供2类日期和时间同步方式:  使用网络提供时间  使用GPS提供时间  以及"自动确定时区"的选项.  Android 之时 ...

  5. Android9.0 本地时区和本地时间的自动更新机制

    Android9.0 本地时区和本地时间的自动更新机制 简介 现在Android通过网络同步时间有两种方式:NITZ和NTP,它们使用的条件不同,可以获取的信息也不一样:勾选自动同步功能后,手机首先会 ...

  6. Linux - 查看、修改、更新系统时间(自动同步网络时间)

    本文以 CentOS 为例,演示如何查看.设置系统时间,以及自动从互联网更新系统时间. 1,查看系统时间 执行 date 命令可以查看当前系统的时间: 2,手动修改系统时间 (1)执行如下命令可以设置 ...

  7. Android 获取应用的安装时间及更新时间

    Android 获取应用的安装时间及更新时间 在工作时遇到一个需求,就是获取应用的安装时间,本来我觉的这个应该是不能获取到的.觉得应用在安装时,并不是由应用自身控制的.所以就直接回复对方说取不到.但是 ...

  8. redis set 超时_Redis 更新(set) key值过期时间被重置

    Redis 更新(set) key值过期时间被重置的问题: 问题描述: 当你在redis中插入一个key值,并且设置了对应过期时间.,当过期时间还没到的时候重新更新 key 值会导致过期时间被刷新. ...

  9. 更新失败无法连接更新服务器未响应,逆水寒客户端更新失败_长时间停滞未响应相关问题解答[图]...

    逆水寒每周四就会进行例行维护,很多小伙伴的电脑在每次更新中都会出现各种各样的相关问题,像一些基本的逆水寒客户端更新失败,长时间停滞未响应相关问题,我们一起来看看官方是如何解答的吧! 一.逆水寒客户端更 ...

最新文章

  1. hashmap value可以为空吗_美团面试题:Hashmap结构,1.7和1.8有哪些区别(最详细解析)...
  2. 比尔·盖茨彻底告别微软,慈善领域「再就业」发挥余热
  3. 开始我的c++学习之路
  4. 第二次启用httpd24调用mysql时出现的错误
  5. Cocos2d-x内存管理研究二
  6. 小师妹学JVM之:JDK14中JVM的性能优化
  7. 使用nginx部署网站
  8. Combiner合并的使用案例
  9. mapinfo在线地图插件_利用开源软件下载地图和影像瓦片数据(修订版)
  10. 在某网课学习前端笔记整理js篇31-ajax
  11. 简单的转盘抽奖html,一个很简单的H5的转盘抽奖的(主要用的是css3的属性)
  12. 前端之路:一款轻量的tooltip插件tippy.js
  13. 小白学习java第11天多态抽象类接口
  14. VS单解决方案多项目多DLL多exe管理
  15. YOLOv3 ubuntu 配置及训练自己的VOC格式数据集
  16. leetcode 笨阶乘
  17. 任天堂官网在哪里修改服务器,任天堂服务器设置
  18. JETSON NANO清除磁盘(SD卡无法被电脑识别)
  19. 微型计算机原理指令系统,微机原理与接口技术 指令系统.ppt
  20. UVa 1252 - Twenty Questions(记忆化搜索,状态压缩dp)

热门文章

  1. 目前计算机技术已经得到了全面的发展,计算机网络技术对人的全面发展的影响.doc...
  2. idm一个网站不能下载两次?这几个办法彻底解决
  3. 2022年全球市场冷冻蛋挞皮总体规模、主要生产商、主要地区、产品和应用细分研究报告
  4. Sheldon Numbers
  5. 再看罗永浩对质王自如
  6. std::true_type和std::false_type详解
  7. 英勇青铜5基金个人策略
  8. MySQL 5.5的安装配置(保姆级别,超级简单)
  9. 洛谷 P1888 三角函数 C语言
  10. 什么是外汇套期保值?套期保值和对冲有什么区别?