You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin iOS如何获取不受休眠与系统日期影响的设备运行时间?

在Xamarin iOS中获取不受影响的系统运行时长

首先,你之前的代码返回常量的原因很明确:kern.boottime返回的并不是运行时长,而是设备启动时的UTC时间戳(以及对应的硬件时钟值),所以你拿到的是一个固定的启动时间点,自然是常量。

接下来针对你的需求,我们分两种场景来解决:

场景1:需要系统实际运行时长(休眠时暂停计数),不受系统日期变更影响

这种场景可以利用mach_absolute_time硬件时钟,结合kern.boottime返回的启动时硬件时钟值来计算,完全和系统时间无关:

  1. 首先定义正确的kern_boottime结构体(因为它包含两个字段:启动UTC时间和启动时的mach_absolute_time值):
[StructLayout(LayoutKind.Sequential)]
private struct KernBootTime
{
    public TimeVal tv;
    public ulong bootAbsoluteTime;
}

[StructLayout(LayoutKind.Sequential)]
public struct TimeVal
{
    public long tv_sec;
    public long tv_usec;
}

[StructLayout(LayoutKind.Sequential)]
private struct MachTimebaseInfo
{
    public uint numer;
    public uint denom;
}
  1. 然后编写方法获取启动时的硬件时钟值,再结合当前硬件时钟计算时长:
[DllImport("libSystem.dylib")]
private static extern int sysctlbyname(string name, IntPtr info, ref uint infoLen, IntPtr newInfo, uint newInfoLen);

[DllImport("libSystem.dylib")]
private static extern ulong mach_absolute_time();

[DllImport("libSystem.dylib")]
private static extern int mach_timebase_info(ref MachTimebaseInfo info);

public static TimeSpan GetSystemUptimeExcludingSleep()
{
    // 获取kern.boottime结构体的长度
    uint infoLen = (uint)Marshal.SizeOf<KernBootTime>();
    IntPtr bootTimePtr = Marshal.AllocHGlobal((int)infoLen);
    
    try
    {
        int result = sysctlbyname("kern.boottime", bootTimePtr, ref infoLen, IntPtr.Zero, 0);
        if (result != 0)
            throw new SystemException("Failed to get boot time");
        
        KernBootTime bootTime = Marshal.PtrToStructure<KernBootTime>(bootTimePtr);
        
        // 获取当前硬件时钟值
        ulong currentAbsoluteTime = mach_absolute_time();
        // 计算差值
        ulong elapsedAbsolute = currentAbsoluteTime - bootTime.bootAbsoluteTime;
        
        // 转换硬件时钟周期为秒数
        MachTimebaseInfo timebaseInfo = new MachTimebaseInfo();
        mach_timebase_info(ref timebaseInfo);
        
        // 计算秒数:(elapsedAbsolute * numer) / denom 得到纳秒数
        double elapsedNanoSeconds = (double)elapsedAbsolute * timebaseInfo.numer / timebaseInfo.denom;
        return TimeSpan.FromNanoseconds(elapsedNanoSeconds);
    }
    finally
    {
        Marshal.FreeHGlobal(bootTimePtr);
    }
}

这个方法返回的是设备启动后实际运行的时长,设备休眠时会暂停计数,但完全不受系统日期修改的影响。

场景2:需要从重启到现在的实际时间流逝(包含休眠时长),但无法完全避免系统日期变更影响

如果你的需求是不管设备是否休眠,都要计算从重启到现在的实际时间长度,那只能依赖系统的实时时钟——因为只有设备的RTC时钟在休眠时会继续走,但RTC时间是可以被用户修改的,所以无法完全避免系统日期变更的影响。不过我们可以尽量优化:

public static TimeSpan GetTotalElapsedSinceRebootIncludingSleep()
{
    uint infoLen = (uint)Marshal.SizeOf<KernBootTime>();
    IntPtr bootTimePtr = Marshal.AllocHGlobal((int)infoLen);
    
    try
    {
        int result = sysctlbyname("kern.boottime", bootTimePtr, ref infoLen, IntPtr.Zero, 0);
        if (result != 0)
            throw new SystemException("Failed to get boot time");
        
        KernBootTime bootTime = Marshal.PtrToStructure<KernBootTime>(bootTimePtr);
        DateTime bootUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(bootTime.tv.tv_sec).AddMicroseconds(bootTime.tv.tv_usec);
        
        // 当前UTC时间减去启动UTC时间
        return DateTime.UtcNow - bootUtc;
    }
    finally
    {
        Marshal.FreeHGlobal(bootTimePtr);
    }
}

这个方法会包含休眠时长,但如果用户修改了系统的UTC时间,结果就会不准确——这是无法避免的,因为iOS没有提供一个既不受系统时间修改影响、又能在休眠时继续计数的时钟API。

总结

  • 如果你需要不受系统日期影响的时长,只能选择场景1的方法,但它会在设备休眠时暂停计数;
  • 如果你需要包含休眠时长的真实流逝时间,只能选择场景2的方法,但它会受系统日期变更的影响;
  • iOS目前没有同时满足两个条件的公开API,你需要根据自己的实际需求做取舍。

内容的提问来源于stack exchange,提问作者Muthukumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:34:47