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

如何配置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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 02:06:02