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

基于Here SDK的导航应用AndroidTest中如何程序化修改模拟器GPS坐标?

程序化模拟Android模拟器GPS坐标的可行方案

我完全理解你的痛点——在集成测试中动态控制GPS位置对导航类应用来说太关键了,尤其是用第三方SDK(比如Here)时,系统级的模拟经常会因为SDK的位置优先级逻辑失效。结合你的场景,这里有几个经过验证的方案:


1. 优先使用Here SDK自带的位置模拟接口

这是最可靠的方案,因为直接对接SDK的位置输入链路,不会被系统设置干扰。Here SDK的Navigation模块通常提供了自定义位置提供者的API,你可以直接注入模拟位置:

@RunWith(AndroidJUnit4::class)
class HereNavigationTest {
    private lateinit var navigationManager: NavigationManager
    private val mockLocationHandler = Handler(Looper.getMainLooper())

    @Before
    fun setup() {
        // 初始化Here SDK的NavigationManager
        navigationManager = NavigationManager.getInstance()
        
        // 创建自定义模拟位置提供者
        val mockLocationProvider = object : LocationProvider {
            override fun startLocationUpdates(listener: LocationListener) {
                // 发送初始位置
                sendMockLocation(listener, 52.520008, 13.404954)
                
                // 可以定时发送连续位置更新(模拟移动)
                mockLocationHandler.postDelayed({
                    sendMockLocation(listener, 52.519558, 13.405245)
                }, 2000)
            }

            override fun stopLocationUpdates() {
                mockLocationHandler.removeCallbacksAndMessages(null)
            }
            
            private fun sendMockLocation(listener: LocationListener, lat: Double, lon: Double) {
                val mockLocation = Location(LocationManager.GPS_PROVIDER).apply {
                    latitude = lat
                    longitude = lon
                    accuracy = 1f
                    time = System.currentTimeMillis()
                    elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
                }
                listener.onLocationUpdated(mockLocation)
            }
        }
        
        // 替换SDK的默认位置提供者
        navigationManager.locationProvider = mockLocationProvider
    }

    @Test
    fun testRouteUpdateOnLocationChange() {
        // 启动导航、验证路线变化等逻辑
        // ...
    }

    @After
    fun teardown() {
        navigationManager.locationProvider = null
        mockLocationHandler.removeCallbacksAndMessages(null)
    }
}

2. 在测试代码中程序化执行ADB emu geo fix命令

如果SDK没有提供自定义位置的接口,你可以通过Instrumentation在测试代码中直接调用模拟器的位置设置命令——这相当于把手动敲的ADB命令整合进测试流程:

private fun setEmulatorLocation(latitude: Double, longitude: Double) {
    // 注意:emu geo fix的参数顺序是【经度在前,纬度在后】,别搞反!
    val command = "emu geo fix $longitude $latitude"
    val instrumentation = InstrumentationRegistry.getInstrumentation()
    
    // 执行shell命令并验证结果
    val result = instrumentation.uiAutomation.executeShellCommand(command)
    val inputStream = result.inputStream
    val output = BufferedReader(InputStreamReader(inputStream)).readText()
    inputStream.close()
    result.close()
    
    if (!output.contains("OK")) {
        throw AssertionError("Failed to set location: $output")
    }
}

// 在测试中调用
@Test
fun testDynamicNavigation() {
    // 设置起点位置
    setEmulatorLocation(52.520008, 13.404954)
    // 等待位置生效(可以用Espresso IdlingResource替代sleep)
    Thread.sleep(1000)
    
    // 启动导航后,动态更新位置
    setEmulatorLocation(52.519558, 13.405245)
    
    // 验证导航路线是否更新
    // ...
}

前置配置:确保模拟位置权限生效

在测试前,你需要确保应用已经获得模拟位置权限,可以在@BeforeClass中预先执行:

@BeforeClass
fun staticSetup() {
    val packageName = InstrumentationRegistry.getInstrumentation().targetContext.packageName
    val instrumentation = InstrumentationRegistry.getInstrumentation()
    
    // 允许应用使用模拟位置
    instrumentation.uiAutomation.executeShellCommand(
        "appops set $packageName android:mock_location allow"
    )
    // 设置当前应用为默认模拟位置应用
    instrumentation.uiAutomation.executeShellCommand(
        "settings put secure mock_location_app $packageName"
    )
}

3. 系统LocationManager模拟(注意局限性)

你之前试过这个方法但失效,大概率是因为Here SDK没有依赖系统的LocationManager,而是使用了自己的位置采集逻辑。如果一定要用这个方案,需要确保:

  • 清单文件中添加<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
  • 测试类中正确启用测试位置提供者:
@Before
fun setupLocationManager() {
    val context = ApplicationProvider.getApplicationContext<Context>()
    val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
    
    if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
        locationManager.addTestProvider(
            LocationManager.GPS_PROVIDER,
            false, false, false, false, true, true, true,
            Criteria.POWER_LOW, Criteria.ACCURACY_FINE
        )
        locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true)
    }
}

但再次强调:如果Here SDK不使用系统LocationManager,这个方法完全无效,优先尝试前两个方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:00:59