安卓怎么同步服务器时间?

在安卓中同步服务器时间有多种方法,我来为你详细介绍几种常见的方案:

1. 使用 HTTP 请求获取服务器时间

public class TimeSyncUtils {
    
    public static long getServerTime(String url) {
        try {
            URL serverUrl = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) serverUrl.openConnection();
            connection.setRequestMethod("HEAD");
            connection.setConnectTimeout(5000);
            connection.setReadTimeout(5000);
            
            long serverTime = connection.getDate();
            connection.disconnect();
            
            return serverTime;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }
    
    // 使用示例
    public static void syncTime() {
        long serverTime = getServerTime("https://your-server.com/api");
        if (serverTime > 0) {
            long deviceTime = System.currentTimeMillis();
            long timeDiff = serverTime - deviceTime;
            
            // 保存时间差,在应用中使用
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
            prefs.edit().putLong("time_offset", timeDiff).apply();
        }
    }
    
    // 获取校准后的时间
    public static long getAdjustedTime() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        long offset = prefs.getLong("time_offset", 0);
        return System.currentTimeMillis() + offset;
    }
}

2. 使用 NTP 协议同步时间(推荐)

添加依赖:

implementation 'commons-net:commons-net:3.8.0'
import org.apache.commons.net.ntp.NTPUDPClient;
import org.apache.commons.net.ntp.TimeInfo;
public class NTPTimeSync {
    
    public static long getNTPTime() {
        NTPUDPClient client = new NTPUDPClient();
        client.setDefaultTimeout(10000);
        
        try {
            client.open();
            InetAddress hostAddr = InetAddress.getByName("pool.ntp.org");
            TimeInfo info = client.getTime(hostAddr);
            info.computeDetails();
            
            Long offset = info.getOffset();
            Long delay = info.getDelay();
            
            if (offset != null) {
                return System.currentTimeMillis() + offset;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            client.close();
        }
        return -1;
    }
}

3. 使用 SNTP 客户端

import android.os.SystemClock;
public class SntpClient {
    private static final int ORIGINATE_TIME_OFFSET = 24;
    private static final int RECEIVE_TIME_OFFSET = 32;
    private static final int TRANSMIT_TIME_OFFSET = 40;
    private static final int NTP_PACKET_SIZE = 48;
    private static final int NTP_PORT = 123;
    private static final int NTP_MODE_CLIENT = 3;
    private static final int NTP_VERSION = 3;
    
    public boolean requestTime(String host, int timeout) {
        try {
            DatagramSocket 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);
            
            buffer[0] = (NTP_MODE_CLIENT | (NTP_VERSION << 3));
            
            long requestTime = System.currentTimeMillis();
            long requestTicks = SystemClock.elapsedRealtime();
            writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);
            
            socket.send(request);
            
            DatagramPacket response = new DatagramPacket(buffer, buffer.length);
            socket.receive(response);
            long responseTicks = SystemClock.elapsedRealtime();
            long responseTime = requestTime + (responseTicks - requestTicks);
            
            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);
            long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2;
            
            // 保存时间偏移量
            saveTimeOffset(clockOffset);
            socket.close();
            return true;
            
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    private long readTimeStamp(byte[] buffer, int offset) {
        long seconds = read32(buffer, offset);
        long fraction = read32(buffer, offset + 4);
        return ((seconds - 2208988800L) * 1000) + ((fraction * 1000L) / 0x100000000L);
    }
    
    private void writeTimeStamp(byte[] buffer, int offset, long time) {
        long seconds = time / 1000L + 2208988800L;
        long fraction = ((time % 1000L) * 0x100000000L) / 1000L;
        write32(buffer, offset, (int) seconds);
        write32(buffer, offset + 4, (int) fraction);
    }
    
    private long read32(byte[] buffer, int offset) {
        byte b0 = buffer[offset];
        byte b1 = buffer[offset + 1];
        byte b2 = buffer[offset + 2];
        byte b3 = buffer[offset + 3];
        
        return ((b0 & 0xFF) << 24) | ((b1 & 0xFF) << 16) | 
               ((b2 & 0xFF) << 8) | (b3 & 0xFF);
    }
    
    private void write32(byte[] buffer, int offset, int value) {
        buffer[offset] = (byte) ((value >> 24) & 0xff);
        buffer[offset + 1] = (byte) ((value >> 16) & 0xff);
        buffer[offset + 2] = (byte) ((value >> 8) & 0xff);
        buffer[offset + 3] = (byte) (value & 0xff);
    }
    
    private void saveTimeOffset(long offset) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        prefs.edit().putLong("ntp_time_offset", offset).apply();
    }
}

4. 定期同步的 Service

public class TimeSyncService extends Service {
    private static final long SYNC_INTERVAL = 30 * 60 * 1000; // 30分钟
    
    private Handler handler = new Handler();
    private Runnable syncRunnable = new Runnable() {
        @Override
        public void run() {
            syncTimeWithServer();
            handler.postDelayed(this, SYNC_INTERVAL);
        }
    };
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        handler.post(syncRunnable);
        return START_STICKY;
    }
    
    private void syncTimeWithServer() {
        // 使用上述任意方法同步时间
        long serverTime = NTPTimeSync.getNTPTime();
        if (serverTime > 0) {
            // 保存时间偏移或直接使用
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
            prefs.edit().putLong("last_sync_time", System.currentTimeMillis())
                       .putLong("time_offset", serverTime - System.currentTimeMillis())
                       .apply();
        }
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

5. 常用 NTP 服务器地址

public class NtpServers {
    public static final String[] SERVERS = {
        "pool.ntp.org",
        "time.google.com",
        "time.apple.com",
        "time.windows.com",
        "cn.pool.ntp.org",  // 中国地区
        "ntp.aliyun.com"    // 阿里云
    };
}

注意事项:

1、权限要求:需要网络权限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

2、时间校准:普通应用无法修改系统时间,只能记录偏移量在应用内使用

3、网络延迟:需要考虑网络延迟对时间同步精度的影响

4、定期同步:建议定期同步以保持时间准确性

选择哪种方法取决于你的具体需求:如果只需要相对时间,HTTP 方法足够;如果需要高精度时间,推荐使用 NTP 协议。

文章摘自:https://idc.huochengrm.cn/fwq/16753.html

评论