React Native应用GPS开关后无法获取位置,Promise无响应求助
我之前做Android原生定位模块时碰到过几乎一模一样的问题,结合你的代码和描述来看,核心问题出在GPS定位的超时处理、状态监听缺失以及请求堆积这几个点上,给你梳理具体的修复方案:
1. 给定位请求添加超时机制(最关键的修复)
你的forceSingleGPSLocationUpdate方法调用了requestSingleUpdate,但完全没有超时处理——一旦GPS搜星慢(比如室内、信号差)或者暂时无法获取位置,Promise就会一直处于pending状态,直到GPS终于拿到位置才会批量resolve,这就是你看到“多次切换后突然全部解决”的原因。
修改代码,添加超时逻辑:
@RequiresPermission( anyOf = [ Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION ] ) fun forceSingleGPSLocationUpdate(promise: Promise) { val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager? if (locationManager === null) { Log.e(LOG_TAG, "Location Manager is null") promise.reject(LOG_TAG, Exception("Location Manager is null")) return } val locationListener = object : LocationListener { override fun onLocationChanged(location: Location?) { // 获取到位置后,先移除监听,避免重复回调 locationManager.removeUpdates(this) if (location === null) { Log.e(LOG_TAG, "Location changed is null") promise.reject(LOG_TAG, Exception("Location changed is null")) return } Log.v(LOG_TAG, "Resolving promise with location") promise.resolve(convertLocationToJSON(location)) } override fun onStatusChanged(provider: String, status: Int, extras: Bundle) { // 当GPS状态变为不可用时,直接reject if (status == LocationProvider.OUT_OF_SERVICE || status == LocationProvider.TEMPORARILY_UNAVAILABLE) { locationManager.removeUpdates(this) promise.reject(LOG_TAG, "GPS provider unavailable") } } override fun onProviderEnabled(provider: String) {} override fun onProviderDisabled(provider: String) { // GPS被关闭时,终止请求 locationManager.removeUpdates(this) promise.reject(LOG_TAG, "GPS provider disabled") } } try { locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, locationListener, null) // 设置10秒超时,可根据需求调整 Handler(Looper.getMainLooper()).postDelayed({ // 检查Promise是否还未完成,是的话就reject if (!promise.isRejected && !promise.isResolved) { locationManager.removeUpdates(locationListener) promise.reject(LOG_TAG, "GPS location request timed out") } }, 10000) } catch (e: SecurityException) { Log.e(LOG_TAG, e.message, e) promise.reject(LOG_TAG, e) } catch (e: Exception) { Log.e(LOG_TAG, e.message, e) promise.reject(LOG_TAG, e) } }
2. 完善LastKnownLocation为空时的降级逻辑
当getLastGPSLocation获取到null时,你直接reject了Promise,但这时候可以自动触发强制获取新位置的逻辑,而不是让请求直接失败:
@RequiresPermission( anyOf = [ Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION ] ) fun getLastGPSLocation(promise: Promise) { val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager? if (locationManager === null) { Log.e(LOG_TAG, "Location Manager is null") promise.reject(LOG_TAG, Exception("Location Manager is null")) return } try { val lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER) if (lastKnownLocation === null) { Log.w(LOG_TAG, "Last known location is null, falling back to fresh GPS request") // 降级请求新位置 forceSingleGPSLocationUpdate(promise) return } Log.v(LOG_TAG, "Resolving promise with last known location") promise.resolve(convertLocationToJSON(lastKnownLocation)) } catch (e: SecurityException) { Log.e(LOG_TAG, e.message, e) promise.reject(LOG_TAG, e) } catch (e: Exception) { Log.e(LOG_TAG, e.message, e) promise.reject(LOG_TAG, e) } }
3. 优化Provider可用性检查
你的areProvidersAvailable()只检查了provider是否enabled,但enabled不代表provider已经就绪(比如GPS刚打开还在搜星),可以优化为检查可用的provider:
fun areProvidersAvailable(): Boolean { val lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager return try { // 获取当前可用的定位provider(true表示只返回enabled且可用的) val availableProviders = lm.getProviders(true) availableProviders.contains(LocationManager.GPS_PROVIDER) || availableProviders.contains(LocationManager.NETWORK_PROVIDER) } catch (ex: Exception) { Log.e(LOG_TAG, ex.toString()) false } }
4. 处理GPS状态变化的广播监听(可选但推荐)
注册一个广播接收器,监听GPS开关状态变化,当GPS从关闭变为打开时,可以主动清理之前pending的请求,或者触发一次定位:
// 在LocationFetcher类中添加广播接收器逻辑 private val gpsStatusReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (intent?.action == LocationManager.PROVIDERS_CHANGED_ACTION) { // GPS状态变化,这里可以根据需求处理,比如通知上层刷新定位状态 Log.d(LOG_TAG, "GPS provider status changed") } } } // 在类初始化时注册广播 init { val filter = IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION) context.registerReceiver(gpsStatusReceiver, filter) } // 记得在合适的时机注销广播(比如React Native模块销毁时) fun destroy() { context.unregisterReceiver(gpsStatusReceiver) }
最后补充:完善Play Services检查
你的checkForPlayServices方法代码不完整,确保完整实现,比如:
internal fun checkForPlayServices(): Boolean { val googleApiAvailability = GoogleApiAvailability.getInstance() val resultCode = googleApiAvailability.isGooglePlayServicesAvailable(context) return resultCode == ConnectionResult.SUCCESS }
这些修改应该能解决你碰到的Promise pending、批量resolve、状态切换无响应的问题,核心就是给定位请求加超时、避免请求堆积、完善状态监听。
内容的提问来源于stack exchange,提问作者Humberd




