如何在设备上模拟位置?

153

如何在物理设备(Nexus One)上模拟我的位置?

我知道你可以在模拟器控制面板中使用此功能,但对于物理设备而言,这并不起作用。


对于台式电脑,您可以创建/安装驱动程序来模拟GPS设备/串口设备,以进行此类测试。也许您可以为Android创建或查找类似的东西。 - AaronLS
6
市场上有几款可以模拟GPS位置的应用程序,比如“位置欺骗器”。 - Vlad
1
商店中还有一个应用程序支持“geo fix”和“geo nmea”命令,就像模拟器一样!https://play.google.com/store/apps/details?id=github.luv.mockgeofix&hl=en - luv
@Iuv 而且它是开源的 https://github.com/luv/mockgeofix - Vlad
21个回答

168

似乎唯一的方法是使用模拟位置提供程序。

您需要在设置中启用模拟位置,并添加

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

将其添加到您的清单文件中。

现在您可以在代码中创建自己的模拟位置提供程序并设置该提供程序的位置。


85
市场上有一个叫做“Fake GPS”的应用程序。如果您在设备上启用模拟位置,它提供了一个漂亮的图形用户界面来设置您的位置。我意识到这不是一个自助解决方案,但对于快速测试来说肯定是一个值得的解决方案。 - Tim Green
2
可能是这个链接 - 相同日期和相关内容:http://pedroassuncao.com/2009/11/android-location-provider-mock/ - DefenestrationDay
实际上,“假GPS”应用程序并不是最好的选择,因为它只能设置位置并随机移动。他们的新版本可以路由,但它只是在路径点之间迭代,所以位置会出现巨大跳跃。Janusz的答案确实正确,你需要创建自己的模拟提供者。我们最近就这样做了,如果有人需要或者这个问题仍然需要帮助,我会发布代码。 - vkinra
7
应该更详细地解释“进入您的代码并创建自己的模拟位置提供程序”的语句。 - Dims
现在,Android Studio 2.3.2 出现了这样的消息:“Mock locations should only be requested in a test or debug-specific manifest file (typically src/debug/AndroidManifest.xml)”,有人知道如何在发布模式下构建它吗? - Huỳnh Ngọc Bang

102

如果您只在开发实验室中使用此手机,则有可能将GPS芯片焊接下来,并直接使用来自其他设备的NMEA序列进行串口馈送。


12
如果你在设备还未焊接时无法生成真实的NMEA序列,有一个很好的选择:将一个实际的外部GPS设备焊接上去,并将整个开发实验室移动到实际的轨道上,让一组跑步者/骑行者/汽车/飞机(根据所需模拟的速度)上方运行。别忘了在开发实验室的屋顶上打孔,以便设备可以接收来自卫星的无线电信号。不用谢。;-) - Stéphane Gourichon
@StéphaneGourichon 更好的做法是将它们全部焊接在一起,制成一个不祥的金属球,并将其用作手机 GPS 的天线。这样肯定能够接收到一些 GPS 信号。 - nurettin
2
真遗憾,这通常是与GSM/HSPA/EDGE/LTE/CDMA相同的IC。这就是讨厌SOC设计的原因 ;) - ewanm89
4
如果你无法将芯片卸下,你可以建造一个装备有与GPS卫星调谐的无线电并以高功率发射以干扰真实信号的无人机中队,然后根据所需欺骗位置轻松计算时间偏移量来伪造位置。请注意,每个无人机都必须在与最近的卫星相同的3个频道上发射,并且这些频道会随着你的位置而改变。此外,请注意,你所在区域可能存在空域限制。 - rupps

32

我希望我有我的电缆方便。我知道你可以telnet到模拟器来改变它的位置。

$ telnet localhost 5554
Android Console: type 'help' for a list of commands
OK
geo fix -82.411629 28.054553
OK

我记不清您是否可以 telnet 到您的设备,但我认为您可以。希望这有所帮助。

您需要使用 adb(Android 调试桥)进行此操作(CLI)。


你在控制台发送任何命令时都会收到权限被拒绝的错误。也许使用终端应用程序可以解决,但我认为这需要 root 访问权限。 - Janusz
只是为了确保我们在同一个页面上:你所说的不是“adb shell”,对吧?我所说的是不同的东西,这是一种设置,允许你设置安卓特定的参数。例如,我知道你可以设置端口转发(即 localhost:1234 转到 phone:1234)而无需 root 访问,因此你可以设置 SOCKS 代理,并通过电缆进行隧道传输,而无需 root 权限。 - Tim Green
你可以使用 adb forward tcp:6000 tcp:23 命令将机器上的 6000 端口转发到设备上的 23 端口,但即使端口显示为开放状态,我也无法连接。在设备上使用 telnet 客户端并连接到 localhost 也会失败。 - Janusz
17
这在模拟器上运行良好,但在实际设备上却不能正常工作。所以它根本没有回答提问者的问题 :( - Matthew Gilliard
4
您的解决方案仅适用于模拟器。问题是关于真实设备的。 - IgorGanapolsky
显示剩余3条评论

29

你可以使用位置服务权限来模拟位置...

"android.permission.ACCESS_MOCK_LOCATION"

然后在你的java代码中,

// Set location by setting the latitude, longitude and may be the altitude...
String[] MockLoc = str.split(",");
Location location = new Location(mocLocationProvider);            
Double lat = Double.valueOf(MockLoc[0]);
location.setLatitude(lat);
Double longi = Double.valueOf(MockLoc[1]);
location.setLongitude(longi);
Double alti = Double.valueOf(MockLoc[2]);
location.setAltitude(alti);

10
mocLocationProvider变量的值是什么? - Victor
2
LocationManager.GPS_PROVIDER - enigma969

25

使用以下代码我已经取得了成功。尽管由于某些原因(即使我尝试了不同的LatLng对),它为我工作。 mLocationManager 是一个连接到 LocationListenerLocationManager

private void getMockLocation()
{
    mLocationManager.removeTestProvider(LocationManager.GPS_PROVIDER);
    mLocationManager.addTestProvider
    (
      LocationManager.GPS_PROVIDER,
      "requiresNetwork" == "",
      "requiresSatellite" == "",
      "requiresCell" == "",
      "hasMonetaryCost" == "",
      "supportsAltitude" == "",
      "supportsSpeed" == "",
      "supportsBearing" == "",

      android.location.Criteria.POWER_LOW,
      android.location.Criteria.ACCURACY_FINE
    );      

    Location newLocation = new Location(LocationManager.GPS_PROVIDER);

    newLocation.setLatitude (/* TODO: Set Some Lat */);
    newLocation.setLongitude(/* TODO: Set Some Lng */);

    newLocation.setAccuracy(500);

    mLocationManager.setTestProviderEnabled
    (
      LocationManager.GPS_PROVIDER, 
      true
    );

    mLocationManager.setTestProviderStatus
    (
       LocationManager.GPS_PROVIDER,
       LocationProvider.AVAILABLE,
       null,
       System.currentTimeMillis()
    );      

    mLocationManager.setTestProviderLocation
    (
      LocationManager.GPS_PROVIDER, 
      newLocation
    );      
}

据我所记,setTestProviderStatus 用于通知/发布/调度一个事件给 LocationManager,以便 GPS 提供程序已经找到了一个修复“现在”,从而可以处理该事件。 - Dr1Ku
"mLocationManager是一个连接到LocationListenerLocationManager"这句话的意思是什么?我该怎么做呢?谢谢。 - Yehuda
这里有一篇文章,介绍如何使用LocationManager服务和LocationListener检查位置更改:http://blog.doityourselfandroid.com/2010/12/25/understanding-locationlistener-android/ - Dr1Ku
2
在方法addTestProvider中,你对true和false的表示方式做出了很好的改进,加1分。 - A.Alqadomi

16

Dr1Ku发布的内容有效。今天我使用了这段代码,但需要添加更多 locs(位置)。

改进:

可选:不要使用LocationManager.GPS_PROVIDER字符串,而是定义自己的常量 PROVIDER_NAME 并使用它。在注册位置更新时,通过标准选择提供程序,而不是直接将其作为字符串指定。

首先:不要调用removeTestProvider,而是先检查是否有要删除的提供程序(以避免IllegalArgumentException异常):

if (mLocationManager.getProvider(PROVIDER_NAME) != null) {
  mLocationManager.removeTestProvider(PROVIDER_NAME);
}

其次:要发布多个位置,您必须为每个位置设置时间:

newLocation.setTime(System.currentTimeMillis());
...
mLocationManager.setTestProviderLocation(PROVIDER_NAME, newLocation);

还有一个使用MockLocationProviders的Google测试:http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/location/LocationManagerProximityTest.java

另一个好的可工作示例可以在这里找到:http://pedroassuncao.com/blog/2009/11/12/android-location-provider-mock/

另一篇好文章:http://ballardhack.wordpress.com/2010/09/23/location-gps-and-automated-testing-on-android/#comment-1358。你也可以在模拟器上找到一些对我实际有效的代码。


有一个很棒的建议是自定义提供者,使用 GPS_PROVIDER 的问题在于,你似乎无法摆脱模拟提供者(更确切地说,你无法切换到正常的 GPS)直到重新启动。同样非常棒的帖子,我会查看参考资料! - Dr1Ku
@Dr1KU,您现在仍然无法切换回普通GPS吗?这个规定在哪里指定了吗? - Kaz Dragon
@KazDragon 那时确实是这样的。据我回忆,在打开“模拟提供程序”后,普通GPS就无法使用了,我无法获取定位(我是直接在设备上进行测试的)。由于Android系统发生了很多变化,现在可能已经不是这种情况了。 - Dr1Ku
@Dr1Ku 啊,好吧,我现在正在做非常类似的事情,并且得到了你描述的类似结果。我希望知道这是否是平台内置的限制还是我只是在某个地方缺少函数调用。我应该把这个变成一个问题。 - Kaz Dragon

9

Android市场上有一些应用程序可以为您的设备指定“模拟GPS位置”。

我在https://market.android.com上搜索到一个名为“我的虚假位置”的应用程序适合我使用。

Paul提到的Mock GPS Provider(位于http://www.cowlumbus.nl/forum/MockGpsProvider.zip)是另一个包含源代码的示例,尽管我无法安装提供的APK(它说Failure [INSTALL_FAILED_OLDER_SDK],可能只需要重新编译)。

为了使用GPS模拟位置,您需要在设备设置中启用它。前往“设置” -> “应用程序” -> “开发”并选中“允许模拟位置”。

然后,您可以使用上述描述的应用程序之一来设置GPS坐标,Google地图和其他应用程序将使用您指定的模拟GPS位置。


虚假GPS更简单。我只是想知道为什么他们要在应用程序权限中要求呼叫者ID :S - Guillaume Massé

8
我已经创建了一个简单的处理程序,模拟从初始位置移动到另一位置的过程。
在连接回调中启动它:
private final GoogleApiClient.ConnectionCallbacks mConnectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
    @Override
    public void onConnected(Bundle bundle) {
        if (BuildConfig.USE_MOCK_LOCATION) {
            LocationServices.FusedLocationApi.setMockMode(mGoogleApiClient, true);
            new MockLocationMovingHandler(mGoogleApiClient).start(48.873399, 2.342911);
        }
    }

    @Override
    public void onConnectionSuspended(int i) {

    }
};

The Handler class :

   private static class MockLocationMovingHandler extends Handler {

    private final static int SET_MOCK_LOCATION = 0x000001;
    private final static double STEP_LATITUDE =  -0.00005;
    private final static double STEP_LONGITUDE = 0.00002;
    private final static long FREQUENCY_MS = 1000;
    private GoogleApiClient mGoogleApiClient;
    private double mLatitude;
    private double mLongitude;

    public MockLocationMovingHandler(final GoogleApiClient googleApiClient) {
        super(Looper.getMainLooper());
        mGoogleApiClient = googleApiClient;
    }

    public void start(final double initLatitude, final double initLongitude) {
        if (hasMessages(SET_MOCK_LOCATION)) {
            removeMessages(SET_MOCK_LOCATION);
        }
        mLatitude = initLatitude;
        mLongitude = initLongitude;
        sendEmptyMessage(SET_MOCK_LOCATION);
    }

    public void stop() {
        if (hasMessages(SET_MOCK_LOCATION)) {
            removeMessages(SET_MOCK_LOCATION);
        }
    }

    @Override
    public void handleMessage(Message message) {
        switch (message.what) {
            case SET_MOCK_LOCATION:
                Location location = new Location("network");
                location.setLatitude(mLatitude);
                location.setLongitude(mLongitude);
                location.setTime(System.currentTimeMillis());
                location.setAccuracy(3.0f);
                location.setElapsedRealtimeNanos(System.nanoTime());
                LocationServices.FusedLocationApi.setMockLocation(mGoogleApiClient, location);

                mLatitude += STEP_LATITUDE;
                mLongitude += STEP_LONGITUDE;
                sendEmptyMessageDelayed(SET_MOCK_LOCATION, FREQUENCY_MS);
                break;
        }
    }
}

希望这能有所帮助...


8
这对我很有帮助(Android Studio):
在手机上禁用GPS和WiFi跟踪。在Android 5.1.1及以下版本中,在开发人员选项中选择“启用模拟位置”。
在src/debug目录下复制您的清单。将以下内容添加到其中(位于“application”标记之外): uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
设置一个名为“map”的地图片段。 在onCreate()中包含以下代码:
lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
ll = new MyLocationListener();
if (lm.getProvider("Test") == null) {
    lm.addTestProvider("Test", false, false, false, false, false, false, false, 0, 1);
}
lm.setTestProviderEnabled("Test", true);
lm.requestLocationUpdates("Test", 0, 0, ll);

map.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
    @Override
    public void onMapClick(LatLng l) {
        Location loc = new Location("Test");
        loc.setLatitude(l.latitude);
        loc.setLongitude(l.longitude);
        loc.setAltitude(0); 
        loc.setAccuracy(10f);
        loc.setElapsedRealtimeNanos(System.nanoTime());
        loc.setTime(System.currentTimeMillis()); 
        lm.setTestProviderLocation("Test", loc);
    }
};

请注意,为了使用“setElapsedRealtimeNanos”方法,您可能需要暂时将模块Gradle文件中的“minSdkVersion”增加到17。
在主活动类中包含以下代码:
private class MyLocationListener implements LocationListener {
    @Override
    public void onLocationChanged(Location location) {
        // do whatever you want, scroll the map, etc.
    }
}

使用AS运行您的应用程序。在安卓6.0及以上版本中,会出现安全异常。现在进入设置中的开发人员选项,并选择“选择虚拟位置应用程序”。从列表中选择您的应用程序。

现在当您在地图上点击时,onLocationChanged() 将携带您点击位置的坐标。

我刚刚弄清楚了这个问题,所以现在我不必手持手机在附近走来走去了。


我认为如果你将这段代码放在服务中,它可以在Android 6上工作。 - Hamza

7

添加到您的清单中

<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
    tools:ignore="MockLocation,ProtectedPermissions" />

模拟位置功能
void setMockLocation() {
    LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
    locationManager.addTestProvider(LocationManager.GPS_PROVIDER, false, false,
            false, false, true, true, true, 0, 5);
    locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);

    Location mockLocation = new Location(LocationManager.GPS_PROVIDER);
    mockLocation.setLatitude(-33.852);  // Sydney
    mockLocation.setLongitude(151.211);
    mockLocation.setAltitude(10);
    mockLocation.setAccuracy(5);
    mockLocation.setTime(System.currentTimeMillis());
    mockLocation.setElapsedRealtimeNanos(System.nanoTime());
    locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, mockLocation);
}

您还需要在设备的开发者设置中启用模拟位置。如果没有此选项,请在上述步骤完成后将“模拟位置应用程序”设置为您的应用程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接