我正在尝试访问Android WebView中可用的HTML Geolocation API(使用SDK版本24)。
主要问题是在JavaScript中对navigator.geolocation.getCurrentPosition()
的调用永远不会返回(既没有错误,也没有位置数据),而在应用程序端,我检查权限并正确传递使用android.webkit.GeolocationPermissions.Callback
类将它们添加到WebView。
更新:在这里要澄清一下,“永不返回”是指从未调用过太过提供的回调navigator.geolocation.getCurrentPosition(success, error)
。
在构建的示例应用程序中,我对此进行了测试(只有一个小的活动托管WebView),我在清单中声明了权限,并在App启动时正确地请求了它们。我看到提示,可以授予或拒绝位置信息的权限。
清单:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
主要形式的代码:
public boolean checkFineLocationPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION) && !mIsPermissionDialogShown) {
showPermissionDialog(R.string.dialog_permission_location);
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
PERMISSION_ACCESS_FINE_LOCATION);
}
return false;
} else {
return true;
}
}
我可以在运行时使用Context.checkSelfPermission()
检查权限,发现相应的权限已授予我的应用。
然后,我尝试在WebView
控件中打开网页。
我在设置中启用了所有必需的选项:
mWebSettings.setJavaScriptEnabled(true);
mWebSettings.setAppCacheEnabled(true);
mWebSettings.setDatabaseEnabled(true);
mWebSettings.setDomStorageEnabled(true);
mWebSettings.setGeolocationEnabled(true);
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
mWebSettings.setSupportZoom(true);
我使用以下WebChromeClient
重载来处理来自JavaScript的地理位置请求:
protected class EmbeddedChromeClient extends android.webkit.WebChromeClient {
@Override
public void onGeolocationPermissionsShowPrompt(String origin,
android.webkit.GeolocationPermissions.Callback callback) {
// do we need to request permissions ?
if (ContextCompat.checkSelfPermission(EmbeddedBrowserActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// this should never happen, it means user revoked permissions
// need to warn and quit?
callback.invoke(origin, false, false);
}
else {
callback.invoke(origin, true, true);
}
}
}
要对此进行测试,我使用以下代码(摘自Mozilla API help page,此处简称):
function geoFindMe() {
function success(position) {}
function error() {}
navigator.geolocation.getCurrentPosition(success, error);
}
我看到的是 JavaScript中对navigator.geolocation.getCurrentPosition(success, error)
的调用永远不会返回。我看到Java中的onGeolocationPermissionsShowPrompt()
方法被正确调用,并且在我检查权限时,我总是得到结果0,即PackageManager.PERMISSION_GRANTED
,因此callback.invoke(origin, true, true)
在每次调用时都会执行。如果尝试几次,则会看到对Java代码的多次调用。不过,在我调用invoke()
之后,JavaScript方面什么也没有发生。
我添加了代码,以使用getOrigins(ValueCallback<Set<String>> callback)
类中的GeolocationPermissions
来检查授予的权限,如here in the documentation所述。我在回调函数中看到允许我的起源请求位置(它们在集合中列出)。
任何想法在这里可能出什么问题吗?
答案 0 :(得分:4)
尝试使用设置超时(source)的选项:
var options = {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
};
navigator.geolocation.getCurrentPosition(success, error, options);
如果失败,则尝试覆盖getCurrentPosition(source):
(function() {
if (navigator.geolocation) {
function PositionError(code, message) {
this.code = code;
this.message = message;
}
PositionError.PERMISSION_DENIED = 1;
PositionError.POSITION_UNAVAILABLE = 2;
PositionError.TIMEOUT = 3;
PositionError.prototype = new Error();
navigator.geolocation._getCurrentPosition = navigator.geolocation.getCurrentPosition;
navigator.geolocation.getCurrentPosition = function(success, failure, options) {
var successHandler = function(position) {
if ((position.coords.latitude == 0 && position.coords.longitude == 0) ||
(position.coords.latitude == 37.38600158691406 && position.coords.longitude == -122.08200073242188))
return failureHandler(new PositionError(PositionError.POSITION_UNAVAILABLE, 'Position unavailable'));
failureHandler = function() {};
success(position);
}
var failureHandler = function(error) {
failureHandler = function() {};
failure(error);
}
navigator.geolocation._getCurrentPosition(successHandler, failureHandler, options);
window.setTimeout(function() { failureHandler(new PositionError(PositionError.TIMEOUT, 'Timed out')) }, 10000);
}
}
})();
作为第三个选项,在EmbeddedChromeClient中用@JavascriptInterface(source)进行注释
还要在代码中的适当位置添加
mWebSettings.setJavaScriptEnabled(true);
//...
mWebSettings.setSupportZoom(true);
webView.addJavascriptInterface(new EmbeddedChromeClient(webView), "injectedObject");
webView.loadData("html here", "text/html", null);
最后一个选择就是使用html中的标签,从磁盘存储中加载html,替换调用函数中的标签,在webView中加载html / string。以前,我在Android上使用这种方法时会感到非常沮丧。然后,您也不必担心https。
答案 1 :(得分:2)
您的情况似乎有2个不同的问题:
getCurrentPosition
永不失败getCurrentPosition
永远不会成功默认值为Infinity,这意味着getCurrentPosition()在该位置可用之前不会返回。
第二点可能很棘手,其中有一个maximumAge表示
PositionOptions.maximumAge属性是一个正的long值,指示可以返回的可能缓存位置的最长使用期限(以毫秒为单位)。如果设置为0,则意味着设备无法使用缓存的位置,而必须尝试检索实际的当前位置。如果设置为Infinity,则设备必须返回缓存的位置,而不管其寿命如何。
0
默认表示该设备将不使用缓存的位置,并会尝试获取真实位置,这可能会导致长时间响应。
您还可以检查此报告,这可能意味着此API在Android上无法正常运行: https://github.com/ionic-team/ionic-native/issues/1958 https://issues.apache.org/jira/browse/CB-13241
* Cordova将地理位置信息留给浏览器。
答案 2 :(得分:1)
这样更改您的许可请求,
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
},0);
这似乎可行。
答案 3 :(得分:1)
对于面向 Android N和更高版本 SDK(API级别> Build.VERSION_CODES.M)的应用程序,仅对源自安全来源(例如HTTPS)的请求调用方法onGeolocationPermissionsShowPrompt (String origin, GeolocationPermissions.Callback callback)
。在非安全来源上,地理位置请求会自动被拒绝。
如果尝试在方法中放置断点或日志,则可以自己缩小问题的范围。
答案 4 :(得分:1)
实际上,navigator
是浏览器全局对象window
的子级。您应该先访问window
,然后调用navigator
。在某些现代浏览器中,除了window
之外,还像全局对象一样显示了一些子对象,例如:location
,navigator
等。
WebView
没有全局对象window
。因此,您可以手动添加它,为此操作,请阅读this medium article。
也许您的代码如下:
my_web_view.evaluateJavascript("javascript: " + "updateFromAndroid(\"" + edit_text_to_web.text + "\")", null);
然后将JavaScript
对象(如window
对象)添加到此评估程序:
my_web_view.addJavascriptInterface(JavaScriptInterface(), JAVASCRIPT_OBJ); //JAVASCRIPT_OBJ: like window, like navigator, like location and etc.
答案 5 :(得分:1)
如果某些人仍然遇到此问题,可以通过为 options 参数设置一些值来使navigator.geolocation.getCurrentPosition()对我有用,如下所示:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
async (p) => {
await this.workMyCurrentPosition(p);
},
(e) => this.navigatorError(e),
{ timeout: 7000, enableHighAccuracy: true, maximumAge: 0 }
);
}
希望它对您有用!
答案 6 :(得分:0)
关于这个主题有很多文章:
navigator.geolocation.getCurrentPosition sometimes works sometimes doesn't
显然,解决方案是检查navigator.geolocation
然后拨打电话:
if(navigator.geolocation) { // dummy call
navigator.geolocation.getCurrentPosition(success, error)
}
答案 7 :(得分:0)
我在Android 8上遇到了此问题。错误回调的调用方式为error.code == 1“ PERMISSION_DENIED”。
https://developers.google.com/web/updates/2016/04/geolocation-on-secure-contexts-only 从Chrome 50的不安全来源中删除了Geolocation API
意思是,您的应用的URL必须以https://开头,才能正常工作。