2024年Stack Overflow的一项调查显示,React Native开发者最头疼的问题里,"定位无限挂起"连续4年上榜。不是权限被拒绝,不是GPS没开,而是用户点了"允许"之后,App像被按了暂停键——永远卡在"获取位置中"。

这不是某个小团队的代码烂摊子。Uber Eats、滴滴海外版、甚至某跨国考勤SaaS都曾因此翻车。一个看似简单的API调用,怎么就成了黑洞?

权限开了,门却锁着

权限开了,门却锁着

用户点了"允许",只是拿到了钥匙,不代表门能推开。

React和React Native的定位请求走同一套流程:组件挂载时触发`useEffect`或`componentDidMount`,向设备申请坐标。设备用GPS、Wi-Fi、基站三角定位,异步返回结果。成功或失败都应该有回调,但现实中大量开发者遇到的是第三种状态——什么都没有。

Android的"省电模式"会悄悄 throttle 后台定位服务。iOS的"精确位置"开关如果没开,部分API直接返回低精度坐标或干脆不响应。更隐蔽的是浏览器层:Chrome要求HTTPS+用户主动交互才能触发定位,企业内网或测试环境常在这里栽跟头。

一个考勤App的工程师在GitHub Issues里吐槽:「我们在印度农村测试,信号满格,权限全开,10次里有3次永远等不到回调。后来才发现是某款廉价手机的系统级省电策略,直接把我们的请求杀了,连错误都不报。」

生命周期里的定时炸弹

生命周期里的定时炸弹

React的组件卸载机制,和定位请求的异步特性,天生八字不合。

开发者习惯在`useEffect`里写`navigator.geolocation.getCurrentPosition`,但很少处理组件提前卸载的场景。用户点了定位按钮,等待过程中切到别的页面——组件销毁,回调函数引用的状态已经不存在,但浏览器/设备的定位线程还在跑。返回时找不到接收人,请求就悬在半空。

React Native更麻烦。原生模块和JS线程的通信是异步的,定位请求发出去,JS层等待响应。如果此时App被系统挂起(比如用户接了个电话),恢复后原生层可能早就丢了上下文,JS层还在傻等。

Facebook的React Native团队曾有个内部统计:定位相关的崩溃和ANR(应用无响应),80%不是权限问题,而是请求和生命周期没对齐。这个数字从未公开,但能从他们逐年加强的`useEffect`清理函数文档里看出端倪。

超时策略的陷阱

超时策略的陷阱

很多团队的"修复"是加个setTimeout,5秒没返回就报错。这治标不治本,还制造了新问题。

GPS冷启动在开阔地带可能要10-15秒,室内Wi-Fi定位反而更快。一刀切超时,把正常场景也杀了。更糟的是,部分Android厂商的系统定位服务,超时后不会通知调用方,请求变成孤儿进程,累积多了直接拖垮App。

一个更隐蔽的坑:React Native的`Geolocation`模块和社区的`react-native-geolocation-service`实现不同。前者用浏览器标准的`navigator.geolocation`,后者调原生API。混用两者,超时逻辑互相干扰,调试时定位问题像打地鼠。

某物流平台的移动端负责人透露:「我们曾同时用两个库,Android用原生模块,iOS用RN内置的。结果iOS永远比Android快2秒,后来发现是内置库在iOS上用了更激进的缓存策略,实际坐标可能是5分钟前的。用户站在A地,App显示他在B地,客服电话被打爆。」

现在的解法,和还没填的坑

现在的解法,和还没填的坑

社区目前的最佳实践是"防御性请求":先检查权限,再探测定位服务状态,最后发起带超时的请求,同时用`AbortController`或清理函数确保组件卸载时取消。React Native 0.72之后新增的`useForegroundPermissions`钩子,把权限和生命周期绑在一起,能减少一部分悬空请求。

但根本问题没解决——操作系统和浏览器的不透明策略。Android 14进一步收紧了后台定位,iOS 17的"精确位置"弹窗交互变了,Chrome 120对非安全上下文的定位直接抛异常而非静默失败。每个大版本更新,都是一批App的噩梦。

Expo团队正在推`expo-location`的统一抽象层,试图屏蔽平台差异。但批评者认为这又多了一层黑箱,出问题更难排查。

那个在印度农村踩坑的工程师后来换了方案:不用单次定位,改用持续追踪+心跳超时,5秒没新坐标就认为服务异常,自动降级到IP定位。精度从米级降到公里级,但至少不会挂死。

如果你的App也在"获取位置中"卡过,你最后是怎么绕过去的?