如何配置WebChromeClient使用Android Location Manager适配无谷歌服务的华为设备
WebChromeClient调用原生Location Manager实现无GMS地理定位方案
核心思路
WebChromeClient默认的地理定位实现依赖Google Play Services的Fused Location Provider,我们可以通过拦截H5的定位权限请求、绕开默认实现,使用Android framework自带的LocationManager完成定位后将结果回传给H5,全程不依赖GMS服务。
实现步骤
1. 配置基础权限与WebView设置
首先在AndroidManifest.xml中添加定位所需权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.INTERNET" />
开启WebView的JavaScript和定位开关:
WebSettings webSettings = webView.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setGeolocationEnabled(true);
2. 重写WebChromeClient拦截定位请求
重写onGeolocationPermissionsShowPrompt方法,不调用父类实现(父类默认走GMS定位逻辑),转而触发原生定位:
webView.setWebChromeClient(new WebChromeClient() { @Override public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { // 先检查动态定位权限 if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1001); return; } // 执行原生定位 executeNativeLocation(new LocationResultListener() { @Override public void onSuccess(double latitude, double longitude) { // 注入JS将定位结果返回给H5,无需修改原有React定位代码 String injectJs = String.format(""" navigator.geolocation.getCurrentPosition = function(successCb) { successCb({ coords: { latitude: %f, longitude: %f, accuracy: 100 } }); } """, latitude, longitude); webView.evaluateJavascript(injectJs, null); callback.invoke(origin, true, false); } @Override public void onFail(int errorCode) { // 对齐HTML5 Geolocation错误码,返回失败信息 String injectJs = String.format(""" navigator.geolocation.getCurrentPosition = function(successCb, errorCb) { errorCb({ code: %d, message: "定位失败" }); } """, errorCode); webView.evaluateJavascript(injectJs, null); callback.invoke(origin, false, false); } }); } });
3. 实现基于LocationManager的原生定位逻辑
完全使用Android系统自带的定位能力,不引入任何GMS相关依赖:
private void executeNativeLocation(LocationResultListener listener) { LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); String provider = LocationManager.NETWORK_PROVIDER; // 优先使用网络定位,无GMS也可正常工作 if (!locationManager.isProviderEnabled(provider)) { provider = LocationManager.GPS_PROVIDER; if (!locationManager.isProviderEnabled(provider)) { // 定位服务未开启,对应HTML5错误码POSITION_UNAVAILABLE listener.onFail(2); return; } } // 添加超时判断,10秒未返回则定位失败 Handler timeoutHandler = new Handler(Looper.getMainLooper()); Runnable timeoutRunnable = () -> { locationManager.removeUpdates(locationListener); // 对应HTML5错误码TIMEOUT listener.onFail(3); }; timeoutHandler.postDelayed(timeoutRunnable, 10000); LocationListener locationListener = new LocationListener() { @Override public void onLocationChanged(@NonNull Location location) { timeoutHandler.removeCallbacks(timeoutRunnable); locationManager.removeUpdates(this); listener.onSuccess(location.getLatitude(), location.getLongitude()); } @Override public void onProviderDisabled(@NonNull String provider) { timeoutHandler.removeCallbacks(timeoutRunnable); locationManager.removeUpdates(this); // 对应HTML5错误码PERMISSION_DENIED listener.onFail(1); } }; // 申请单次定位更新 locationManager.requestSingleUpdate(provider, locationListener, Looper.getMainLooper()); } // 自定义定位结果回调接口 interface LocationResultListener { void onSuccess(double latitude, double longitude); void onFail(int errorCode); }
注意事项
- 动态权限申请结果需要在Activity的
onRequestPermissionsResult方法中处理,权限授权通过后重新触发定位逻辑 - 若React应用有封装好的全局定位回调方法,可直接调用对应方法传入坐标,无需重写
navigator.geolocation - 不要引入任何
com.google.android.gms:play-services-location相关依赖,全部使用android.location包下的系统类
内容的提问来源于stack exchange,提问作者Vikas Roy
相关产品推荐
相关产品推荐

