Android6.0(Api23)以后 WebView定位失效问题

Android WebView 加载网页url中,H5通过js获取位置定位信息。

	/**
 * 地图相关
 */
window.init = function() {
	var map = new AMap.Map('mapContainer', {
		resizeEnable: true,
		zoom: 14
	});
	/*获取实时定位*/
	map.plugin('AMap.Geolocation', function() {
		geolocation = new AMap.Geolocation({
			enableHighAccuracy: true, //是否使用高精度定位,默认:true
			timeout: 10000, //超过10秒后停止定位,默认:无穷大
			maximumAge: 0, //定位结果缓存0毫秒,默认:0
			convert: true, //自动偏移坐标,偏移后的坐标为高德坐标,默认:true
			showButton: true, //显示定位按钮,默认:true
			buttonPosition: 'LB', //定位按钮停靠位置,默认:'LB',左下角
			buttonOffset: new AMap.Pixel(10, 20), //定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)
			showMarker: true, //定位成功后在定位到的位置显示点标记,默认:true
			showCircle: true, //定位成功后用圆圈表示定位精度范围,默认:true
			panToLocation: true, //定位成功后将定位到的位置作为地图中心点,默认:true
			zoomToAccuracy: true //定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
		});
		map.addControl(geolocation);
		geolocation.getCurrentPosition();
		AMap.event.addListener(geolocation, 'complete', onComplete); //返回定位信息
		AMap.event.addListener(geolocation, 'error', onError); //返回定位出错信息
	});
	/*获取实时定位成功*/
	function onComplete(data) {
		var lng_my = data.position.lng;
		var lat_my = data.position.lat;
		var address_my = data.formattedAddress;
		var address_province = data.addressComponent.province;
		var address_city = data.addressComponent.city;
		var address_district = data.addressComponent.district;
		var address_township = data.addressComponent.township;
		address_my = address_my.replace(address_province, '').replace(address_city, '').replace(address_district, '').replace(address_township, '');

		var lnglat = gcj02towgs84(lng_my, lat_my);
		var lnglat = gcj02towgs84(lng_my, lat_my);

		$('#lng').val(lnglat[0]);
		$('#lat').val(lnglat[1]);
		$('#addrDetail').val(address_my);
	}
	/*获取实时定位失败*/
	function onError(data) {
		mui.alert('请打开GPS!', ' ', '确定', function() {
			removeDrop();
		}, 'div');
		$('.mui-popup').addClass('new-popup');
	}
	/*点击*/
	var geocoder = null;
	var clickHandler = function(e) {
		if(!geocoder) {
			geocoder = new AMap.Geocoder({
				city: "济南", //城市设为北京,默认:“全国”
				radius: 1000 //范围,默认:500
			});
		}
		var lnglat = [];
		lnglat.push(e.lnglat.getLng())
		lnglat.push(e.lnglat.getLat())
		geocoder.getAddress(lnglat, function(status, result) {
			if(status === 'complete' && result.regeocode) {
				var address = result.regeocode.formattedAddress;
				var address_province = result.regeocode.addressComponent.province;
				var address_city = result.regeocode.addressComponent.city;
				var address_district = result.regeocode.addressComponent.district;
				var address_township = result.regeocode.addressComponent.township;
				address = address.replace(address_province, '').replace(address_city, '').replace(address_district, '').replace(address_township, '');
				var btnArray = ['取消', '确认'];
				mui.confirm('选中“' + address + '”?', ' ', btnArray, function(ee) {
					$('.mui-popup-backdrop').hide()
					if(ee.index == 1) {
						var lnglat84 = gcj02towgs84(lnglat[0], lnglat[1]);
						$('#lng').val(lnglat84[0]);
						$('#lat').val(lnglat84[1]);
						//回填位置信息
						$('#addrDetail').val(address);
						$('#mapContainer').addClass('mapContainer').removeClass('mapContainer-fullScreen');
						returnPageOne();
						addModel.step = 1;
						hideErrorSymbol('addrDetail', '../img/icon/icon-located.png');
						//显示第三页的单元信息
						showUnitAmountInfo();

					} else {

					}
				}, 'div');
				$('.mui-popup').addClass('new-popup');
			} else {
				//					alert(JSON.stringify(result))
			}
		});
	};
	// 绑定地图单击事件
	map.on('click', clickHandler);
	map.on('resize', function() {
		map.setZoom(14);
	});
}

  以前也进行过H5需要位置信息的功能开发,不过以往的方案都是使用 Android 原生定位(集成高德/百度SDK),再将位置信息传给H5来实现,不过这次由于考虑到apk大小的问题(不再集成高德SDK)以及“懒”(不用再写定位的代码),所以就准备都让H5来做。本来认为这个方案应该是很简单的,没想到遇到了一个大坑——H5无法获取到位置信息。

1. 首先,想要获取位置信息,一定要来点定位权限!见代码

<!-- 网络权限,加载网络网页需要联网 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 粗略定位权限,允许一个程序通过网络获取粗略的位置 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 精确定位权限,允许一个程序访问精确位置(GPS定位) -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

2. 在刚进入页面时,一般为onCreate()方法中,动态申请权限(我就不信你的app没有适配Android 6.0+)。在获取到权限后,再调用webview的loadUrl()比较稳妥~
3. webview相关代码

    WebSettings webSettings = mWebView.getSettings();
    // 允许调用 JS,因为网页地图使用的是 JS 定位
    webSettings.setJavaScriptEnabled(true);
    //启用数据库
    webSettings.setDatabaseEnabled(true);
    //启用地理定位,默认为true
    webSettings.setGeolocationEnabled(true);
    //设置定位的数据库路径
    String dir = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
    webSettings.setGeolocationDatabasePath(dir);
    //开启DomStorage缓存
    webSettings.setDomStorageEnabled(true);
    mWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
                callback.invoke(origin, true, false);
                super.onGeolocationPermissionsShowPrompt(origin, callback);
            }

        });

  以上代码,自我感觉setJavaScriptEnabled()、setGeolocationEnabled()、setWebChromeClient()十分重要,其余的应该是可写可不写吧,不过没测试过。
4. 初步总结
  第1步和第2步,主要是用于引导用户开启设备定位权限;第3步则是引导用户开启浏览器定位权限。
  可但是!经过我代码的断点,设备定位权限获取到了,为什么没有走WebChromeClient中的onGeolocationPermissionsShowPrompt()方法?!!why??
5. 查阅资料
  于是,我各种百度google,各种推荐将targetSdkVersion降到23,终于,我在官方讲解中发现:
在这里插入图片描述
  重点在这里:
  Note that for applications targeting Android N and later SDKs (API level > Build.VERSION_CODES.M) this method is only called for requests originating from secure origins such as https. On non-secure origins geolocation requests are automatically denied.
  翻译如下:
  注意,对于针对Android N和以后的SDKs (API级别> Build.VERSION_CODES.M)的应用程序,此方法仅对来自安全源(如https)的请求调用。在非安全源上,将自动拒绝地理位置请求。
好吧,我的H5的url是“http”开头,被自动屏蔽了。
6. 总结
  WebView定位的解决方案:
  方案一:webview的定位,乖乖的使用原生的定位方法,之后将位置信息传递到H5~
  方案二:使用高德地图的辅助H5页面定位,已经测试过,可行!
  方案三:降低targetSdkVersion版本到23,要求较低的懒人法
  方案四:升级http到https

发布了28 篇原创文章 · 获赞 38 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_36158551/article/details/105294029