问题描述
我正在尝试访问Android WebView中提供的HTML Geolocation API(使用SDK版本24).
I am trying to access the HTML Geolocation API available in Android WebView (using SDK version 24).
主要问题是在JavaScript中对navigator.geolocation.getCurrentPosition()
的调用永远不会返回(既不包含错误,也不包含位置数据),而在应用程序端,我检查权限并将其正确传递给使用android.webkit.GeolocationPermissions.Callback
类的WebView.
The main problem is that the call to navigator.geolocation.getCurrentPosition()
in JavaScript never returns (neither with an error, nor with position data), while on application side I check for permissions and properly pass them to WebView using android.webkit.GeolocationPermissions.Callback
class.
更新:在这里要澄清一下,永不返回"表示没有调用过任何提供过的回调navigator.geolocation.getCurrentPosition(success, error)
.
UPDATE: Just to clarify here, by "never returns" I mean that none of the too supplied callbacks navigator.geolocation.getCurrentPosition(success, error)
are ever called.
在构建的示例应用程序中,我对此进行了测试(只有一个托管WebView的小活动),我在清单中声明了权限,并在App启动时正确地请求了这些权限.我看到提示,可以授予或拒绝位置信息的权限.
In a sample app I built to test this (with just one small activity hosting WebView) I declare the permissions in manifest and request them properly on App start. I see the prompt and can grant or deny permission to location information.
清单:
<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()
在运行时检查权限,并且看到相应的权限已授予我的应用.
I can check for permissions during runtime using Context.checkSelfPermission()
and I see that the respective permissions are granted to my app.
然后,我尝试在WebView
控件中打开网页.我在设置中启用了所有必需的选项:
Then I try to open a web page in a WebView
control.I enable all required options in the settings:
mWebSettings.setJavaScriptEnabled(true);
mWebSettings.setAppCacheEnabled(true);
mWebSettings.setDatabaseEnabled(true);
mWebSettings.setDomStorageEnabled(true);
mWebSettings.setGeolocationEnabled(true);
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
mWebSettings.setSupportZoom(true);
我使用以下WebChromeClient
重载来处理来自JavaScript的地理位置请求:
I use the following WebChromeClient
overload for handling geolocation requests from 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帮助页,此处缩短):
To test this I use the following code (taken from Mozilla API help page, shortened here):
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方面什么也没发生.
What I see is that the call tonavigator.geolocation.getCurrentPosition(success, error)
in JavaScript never returns. I see that onGeolocationPermissionsShowPrompt()
method in Java gets properly called and as I check for permissions there I always get the result 0, i.e. PackageManager.PERMISSION_GRANTED
, so callback.invoke(origin, true, true)
is executed on every call. If I try several times, I see several calls to my Java code. Still, nothing happens on the JavaScript side here after I call invoke()
.
我添加了代码,以使用GeolocationPermissions
类中的getOrigins(ValueCallback<Set<String>> callback)
调用来检查授予的权限,如文档中的.我在回调函数中看到允许我的来源请求位置(它们在列表中列出).
I added the code to check for granted permissions using the invocation of getOrigins(ValueCallback<Set<String>> callback)
in GeolocationPermissions
class, as described here in the documentation. I see in the callback that my origins are allowed to request locations (they are listed in the set).
任何想法在这里可能出什么问题吗?
Any ideas what might be wrong here?
推荐答案
尝试使用设置超时的选项(源):
Try with options to set timeout (source):
var options = {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
};
navigator.geolocation.getCurrentPosition(success, error, options);
如果失败,则尝试覆盖getCurrentPosition(源):
If it fails then try to override 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);
}
}
})();
作为第三个选项,使用@JavascriptInterface进行注释(源)在EmbeddedChromeClient中
As a third option annotate with @JavascriptInterface (source) in EmbeddedChromeClient
还要在代码中的适当位置添加
Also add at the proper place in your code:
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.
The last option is just to use tags in html, load the html from disk storage, replace tags in the calling function, load the html/string in the webView. I have used this approach before in Android when positioning frustrated me too much. Then you don't have to worry about https either.
这篇关于navigator.geolocation.getCurrentPosition()永远不会在Android的WebView中返回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!