навигатор.геолокация.getCurrentPosition () никогда не возвращается в WebView на Android

я пытаюсь получить доступ к API геолокации HTML, доступному в Android WebView (используя SDK версии 24).

главная проблема в том, что вызов navigator.geolocation.getCurrentPosition() в JavaScript никогда не возвращает (ни с ошибкой, ни с данными о положении), в то время как на стороне приложения я проверяю разрешения и правильно передаю их в WebView, используя android.webkit.GeolocationPermissions.Callback класса.

обновление: просто чтобы уточнить здесь, под "никогда не возвращается" я имею в виду, что ни один из поставляемые обратные вызовы navigator.geolocation.getCurrentPosition(success, error) когда-нибудь.

в примере приложения, которое я построил, чтобы проверить это (только с одним небольшим хостингом активности WebView), я объявляю разрешения в манифесте и запрашиваю их должным образом при запуске приложения. Я вижу приглашение и могу предоставить или запретить разрешение на информацию о местоположении.

Манифест:

<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, укорачиваются здесь):

function geoFindMe() {
  function success(position) {}
  function error() {}
  navigator.geolocation.getCurrentPosition(success, error);
}

я вижу, что вызовnavigator.geolocation.getCurrentPosition(success, error) в JavaScript никогда не возвращает. Я вижу это onGeolocationPermissionsShowPrompt() метод в Java правильно вызывается, и когда я проверяю разрешения, я всегда получаю результат 0, т. е. PackageManager.PERMISSION_GRANTED, так что callback.invoke(origin, true, true) выполняется при каждом вызове. Если я попробую несколько раз, я вижу несколько вызовов моего кода Java. Тем не менее, ничего не происходит на стороне JavaScript здесь после вызова invoke().

я добавил код для проверки предоставленных разрешений с помощью вызова getOrigins(ValueCallback<Set<String>> callback) на GeolocationPermissions класс, как описано здесь в документации. Я вижу в обратном вызове, что моим источникам разрешено запрашивать местоположения (они перечислены в наборе).

есть идеи, что здесь может быть не так?

6 ответов


попробуйте с параметрами, чтобы установить тайм-аут (источник):

var options = {
    enableHighAccuracy: true,
    timeout: 10000,
    maximumAge: 0
};

navigator.geolocation.getCurrentPosition(success, error, options);

Если это не удается, попробуйте переопределить getCurrentPosition (источник):

(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

добавить в нужное место в коде:

mWebSettings.setJavaScriptEnabled(true);
//...
mWebSettings.setSupportZoom(true);
webView.addJavascriptInterface(new EmbeddedChromeClient(webView), "injectedObject");
webView.loadData("html here", "text/html", null);

последний вариант - просто использовать теги в html, загружать html из дискового хранилища, заменять теги в вызове функция, загрузите html / строку в webView. Я использовал этот подход раньше в Android, когда позиционирование расстраивало меня слишком много. Тогда вам не придется беспокоиться ни о https.


похоже, что в вашем случае есть 2 разных проблемы:

  1. getCurrentPosition никогда не
  2. getCurrentPosition никогда не удастся

первый пункт может быть просто потому, что метод бесконечно автоотключение

значение по умолчанию-Infinity, что означает, что getCurrentPosition () не вернется, пока позиция не будет доступна.

второй пункт может быть сложным, есть парам maximumAge что означает

PositionOptions.свойство maximumAge-положительное длинное значение, указывающее максимальный возраст в миллисекундах возможной кэшированной позиции, приемлемой для возврата. Если задано значение 0, это означает, что устройство не может использовать кэшированную позицию и должно попытаться получить реальную текущую позицию. Если задано значение Infinity, устройство должно возвращать кэшированную позицию независимо от ее возраста.

0 по умолчанию означает это устройство не будет использовать кэшированную позицию и попытается получить реальную, и это может быть проблемой для длительного ответа.

также вы можете проверить эту сообщению, которое может означать, что этот API не работает очень хорошо на Андроид: https://github.com/ionic-team/ionic-native/issues/1958 https://issues.apache.org/jira/browse/CB-13241

*Кордова оставляет геолокации материал для браузера.


измените запрос на разрешение следующим образом,

ActivityCompat.requestPermissions(this, new String[]{
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION
    },0);

Это, кажется, работает.


на самом деле navigator является дочерним глобальным объектом браузера,window. вы должны сначала получить доступ к вызов navigator. В некоторых современных браузерах, помимо window некоторые дочерний объект представлен как глобальный объект, например: location, navigator и т. д.

на WebView не имеет глобального объекта window. Так что вы можете добавить его вручную, для этого действия, пожалуйста, прочитайте эта среда статьи.

возможно, ваш код будет похож ниже:

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.

для приложения Android N и более поздние версии SDKs (уровень API > сборка.VERSION_CODES.М), метод onGeolocationPermissionsShowPrompt (String origin, GeolocationPermissions.Callback callback) вызывается только для запросов, исходящих из безопасных источников, таких как HTTPS. В незащищенных источниках запросы геолокации автоматически отклоняются.

вы могли бы сузить свою проблему самостоятельно, если бы вы попытались поместить точку останова или журнал внутри метода.

у вас есть два опции:

  1. цель API более низкого уровня, что, очевидно, намного проще, но не очень ценится.
  2. настройка SSL на вашем веб-сайте.

есть обширный пост на эту тему:

навигатор.геолокация.getCurrentPosition иногда работает иногда не

очевидно, решением является проверка на navigator.geolocation затем звоните:

if(navigator.geolocation) { // dummy call
    navigator.geolocation.getCurrentPosition(success, error)
}