navigator.geolocation.getCurrentPosition()永远不会在Android的WebView中返回

时间:2018-08-06 22:37:37

标签: javascript android android-webview w3c-geolocation

我正在尝试访问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所述。我在回调函数中看到允许我的起源请求位置(它们在集合中列出)。

任何想法在这里可能出什么问题吗?

8 个答案:

答案 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个不同的问题:

  1. getCurrentPosition永不失败
  2. getCurrentPosition永远不会成功

第一点可能只是因为method具有无限的timeout

  

默认值为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)。在非安全来源上,地理位置请求会自动被拒绝。

如果尝试在方法中放置断点或日志,则可以自己缩小问题的范围。

您有两个选择:

  1. 以较低级别的API为目标,这显然容易得多,但并没有真正受到赞赏。
  2. 在您的网站中设置SSL。

答案 4 :(得分:1)

实际上,navigator是浏览器全局对象window的子级。您应该先访问window,然后调用navigator。在某些现代浏览器中,除了window之外,还像全局对象一样显示了一些子对象,例如:locationnavigator等。

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://开头,才能正常工作。