转载请注明出处: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() {            @Override            public 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 thread        mHandler.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 thread        mHandler.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);        }        @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;            }        }    }

接收请求类型: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 time        if (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 outdated            if (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 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.                //3.2.1、如果开机第一次同步或者最新时间与当前时间差别超过mTimeErrorThresholdMs即25秒,则进行时间设定。否则认定新同步时间与当前时间差别不大,不覆盖当前时间。                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                    //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 later                    mTryAgainCounter = 0;                    resetAlarm(mPollingIntervalMs);                }                return;            }        }        //4、如果刚更新时间不久,则10天后再发起时间同步请求。        resetAlarm(mPollingIntervalMs);    }

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

public boolean forceRefresh() {        if (mServer == null) {            // missing server, so no trusted time available            return 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 byte            buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);            // get current time and write it to the request packet            long requestTime = System.currentTimeMillis();            long requestTicks = SystemClock.elapsedRealtime();            writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);            socket.send(request);            // read the response            DatagramPacket response = new DatagramPacket(buffer, buffer.length);            socket.receive(response);            long responseTicks = SystemClock.elapsedRealtime();            long responseTime = requestTime + (responseTicks - requestTicks);            // extract the results            long 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 = skew            long 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时间同步,一旦发起成功的时间同步,时间数据会存在内存中,并根据当前机器运行时间来设定最新的时间。

更多相关文章

  1. Android】Retrofit网络请求参数注解,@Path、@Query、@QueryMap...
  2. 【Android游戏开发十五】关于Android(安卓)游戏开发中 OnTouchEv
  3. RxJava教程(四)在Android中使用RxJava
  4. 深入浅出RxJava1(四:在Android中使用响应式编程)
  5. android 双击事件监听
  6. Android(安卓)Volley使用之二:Volley请求网络图片
  7. Android(安卓)Http请求方法汇总
  8. android 中本地java代码与html交互总结
  9. android通知栏:Service每分钟请求一次服务器

随机推荐

  1. 7.0、Android(安卓)Studio命令行工具
  2. Xamarin.Android教程:模拟器选择
  3. android Service之一:不需和Activity交互
  4. Fragment、FragmentActivity 和Actvity区
  5. 【Android】快速切换到主线程更新UI的几
  6. Android仿百度地图小度语音助手的贝塞尔
  7. [置顶] android LayoutInflater、setCont
  8. 判断环境为iOS或Android
  9. Android开发中Activity属性设置小结
  10. android 使用 kotlin lambda 表达式,可以