使用 LocationManager.SetProviderLocation 模拟位置,如何使位置完整

Posted

技术标签:

【中文标题】使用 LocationManager.SetProviderLocation 模拟位置,如何使位置完整【英文标题】:Mocking a location using LocationManager.SetProviderLocation, how can I make the Location complete 【发布时间】:2013-10-21 16:04:56 【问题描述】:

我试图通过创建一个 Location 实例然后使用 LocationManager.SetTestProviderLocation(string providerName, Locatin location) 方法来模拟我的位置。

例如:

Location location = new Location(providerName)

    Latitude = 51.20958,
    Longitude = 2.92372,
    Accuracy = 100.0f,
    Time = DateTime.Now.Ticks,
    Altitude = 1.0,
    Speed = 0.0f,
    Bearing = 0.0f,
    Provider = providerName,
;

locationManager.SetTestProviderLocation(providerName, location);

这总是会导致异常:

“位置对象不完整,缺少时间戳或准确性? 位置[gps 51.209580,2.923720 acc=1 et=?!? alt=1.0 水平=0.0 熊=0.0]"

我找到了一些关于主题here的信息。 作者提到LocationManger检查一个isComplete(方法)来确定Location是否完整。

我希望该类的单声道版本具有 IsComplete 属性 和 MakeComplete 方法,但它们不存在(或者至少我找不到它们)

仔细看看android SDK source code:

public void makeComplete() 

        if (mProvider == null) mProvider = "?";
        if (!mHasAccuracy) 
            mHasAccuracy = true;
            mAccuracy = 100.0f;
        
        if (mTime == 0) mTime = System.currentTimeMillis();
        if (mElapsedRealtimeNanos == 0) mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();

我猜“et=?!?”异常中的值指的是 mElapsedRealtimeNanos 字段并且由于我无法知道访问该字段的方法,因此我无法进行模拟工作。

我是否遗漏了什么,这是 Mono for Android 的缺失功能还是 有没有其他方法可以让它工作。

注意:我知道有一些方法可以使用 DDMS 设置虚假位置, 但我正在寻找一种自动化位置模拟的方法

更新

发布此问题后不久,我意识到我的目标不是 API 级别 16。 因此,适用于 Android 的 Mono 没有公开 ElapsedRealtimeNanos 属性。

切换到 API 级别 16 后,该属性变为可用。然而,它没有, 提供一个 MakeComplete 方法。 (也许这是一个错误/缺失的功能)。

所以我做了一个快速的解决方案来验证行为:

Location location = new Location(providerName)

    Longitude = locations[position, 0],
    Latitude = locations[position, 1],                    
    Accuracy = 100.0f,
    Time = DateTime.Now.Ticks,
    Altitude = 1.0,
    Speed = 0.0f,
    Bearing = 0.0f,
    Provider = providerName,
    ElapsedRealtimeNanos = (long)(TimeSpan.FromTicks(DateTime.Now.Ticks)
                                          .TotalMilliseconds * 1000000)
;

虽然这解决了 Jelly Bean 设备(4.1 或更高版本)的问题,但代码 会在旧设备上中断。这就是@Cheesebaron 建议的解决方案发挥作用的地方。

【问题讨论】:

【参考方案1】:

您可能需要通过 JNI 访问 makeComplete,代码如下:

public class MyLocation : Location

    protected MyLocation(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
    
    

    public MyLocation(Location l) : base(l)
    
    

    public MyLocation(string provider) : base(provider)
    
    

    public void MakeComplete()
    
        if ((int)Build.VERSION.SdkInt >= 16)
        
            var locationClass = JNIEnv.FindClass("android/location/Location");
            var makeComplete = JNIEnv.GetMethodID(locationClass, "makeComplete", "()V");
            JNIEnv.CallVoidMethod(Handle, makeComplete);
        
    

然后只需使用 MyLocation 的实例即可调用 MakeComplete。但是它只能在 API 16 上运行。

【讨论】:

您好,谢谢回答,同时我弄清楚了发生了什么,将在此处发布 其实没有,你的解决方案是正确的。我将更新我的答案以显示我的发现,但将您的答案标记为解决方案,显然是要走的路! makeComplete 根据文档仅用于向后兼容,并且仅存在于 API 16 及更高版本中。 我明白了……那我的解决方案呢?这是在更新设备上进行的方式,还是他们提出了不同的建议,因为我还没有找到。 文档说这是你想要做的。所以我想坚持下去。

以上是关于使用 LocationManager.SetProviderLocation 模拟位置,如何使位置完整的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)