Не удается получить текущую позицию без GPS на основе реакции с navigator.geolocation

Краткое резюме после обсуждения и ответы:

используя EXPO SDK вы не можете получить местоположение устройства без предоставления FINE_LOCATION в Android. FINE_LOCATION - единственный метод для определения местоположения, поэтому вы не можете получить hardware.network.location. Это означает: с android> 6 вы не можете получить текущее местоположение, используя WIFI/мобильные сети, вы должны включить Location.

Обсуждение github на выставке: https://github.com/expo/expo/issues/1339

Начальная проблема:

Я работаю над собственным приложением реакции, используя expo и response-native-карты, и мне нужно получить широту и долготу текущей позиции пользователя.

Я использую Navigation.geolocation API для этого

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

Проблема в том, что когда приложение работает с expo на androiod> 6, я получаю эту ошибку:

21:50:53: Завершено сборка JavaScript-пакета за 449 мс 21:50:55: Службы определения местоположения отключены - node_modules\реагировать-native\Libraries\BatchedBridge\NativeModules.js: 80: 57 in - node_modules\реагировать-native\Libraries\BatchedBridge\MessageQueue.js: 347: 19 в __invokeCallback -... еще 4 стековых фрейма из внутренних компонентов фреймворка

В IO и Android <= 5 это прекрасно работает.

Вот код компонента:

class MyComponent extends Component {

    componentWillMount = () => {    
            this.getCurrentLocation();    
    }
    getCurrentLocation = () =>
         navigator.geolocation.getCurrentPosition(
            (position) => {
                let currentUserPosition = position.coords;
                alert(JSON.stringify(currentUserPosition));
            },
            (error) => {
                console.log(error);
            },
            {
                enableHighAccuracy: false,
                timeout: 20000,
                maximumAge: 0,
                distanceFilter: 10
            }
        );

}

И это мои зависимости package.json:

"dependencies": {
    "expo": "23.0.4",
    "native-base": "^2.3.5",
    "react": "16.0.0",
    "react-native": "0.50.4",
    "react-native-extend-indicator": "^0.1.2",
    "react-native-maps": "^0.19.0",
    "react-native-maps-directions": "^1.3.0",
    "react-native-router-flux": "4.0.0-beta.21",
    "react-navigation": "1.0.0-beta.21",
    "react-navigation-redux-debouncer": "^0.0.2",
    "react-redux": "^5.0.6",
    "redux": "^3.7.2",
  }

Я ожидаю, что navigator.geolocation получит местоположение на основе сетевого провайдера в этой ситуации (без gps), в спецификации сказано, что..

я также попробовал с API геолокации expo (попробовал этот пример: https://snack.expo.io/@schazers/expo-map-and-location-example), и с выключенным GPS я не могу получить свое местоположение.,

так.. есть ли способ добиться того, чего я хочу? я что-то упустил?

РЕДАКТИРОВАТЬ (EXPO CONTRIBUTOR ANSWER):

Я опубликовал то же самое на выставке github (https://github.com/expo/expo/issues/1339), по их мнению, невозможно получить текущую позицию с помощью navigator.geolocation без какого-либо уровня местоположения, включенного в Android устройство... так что... единственное, что может случиться, это то, что в версиях Android старше 5 по умолчанию включено местоположение, и вы можете включить только GPS, а в версиях 6 и выше вы должны указать уровень местоположения.

есть идеи?

РЕДАКТИРОВАТЬ 2 (ВАЖНО!):

Я подтвердил это:

enter image description here

это настройки безопасности устройства Android 6, по умолчанию оно использует GPS, я думаю, что Android 5 или ниже не делает, так что вот в чем дело... когда я использую второй вариант, он получает мне местоположение!

Дело в том, что принуждение пользователя к включению Location похоже на "эй, используй свой GPS!", и есть много приложений, которые дают вам приблизительную позицию без включения Location (как, например, Uber), поэтому вопрос в том, Есть ли способ, что ТОЛЬКО с помощью Wi-Fi получить местоположение с этим API?

Ответ 1

Проблема, с которой вы здесь работаете, связана с EXPO, потому что API запроса местоположения изменился после Android 5 (API 21).

До API 21 вам нужно было добавить к ACCESS_FINE_LOCATION (как ACCESS_FINE_LOCATION Wi-Fi/Network и GPS), так и ACCESS_COARSE_LOCATION (только разрешения GPS) разрешения. Однако, начиная с Android 5, apis меняется на android.hardware.location.network и android.hardware.location.gps.

Когда ваши целевые пользователи включают Android 5 + / -. вам нужно добавить оба типа разрешений в ваш манифест Android, например:

<manifest ... >
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <!-- for Android 5.0 (API level 21) or higher. -->
    <uses-feature android:name="android.hardware.location.gps" />
    <uses-feature android:name="android.hardware.location.network" />
</manifest>

Тем не менее, кажется, что люди в Экспо, только включены, новое разрешение в манифесте Android. Так что вам нужно изменить манифест Android. Однако expo предоставляет доступ только к чистой части программы JS, а не к собственным кодам. В результате вам необходимо отсоединиться от EXPO, чтобы добавить собственные модули или изменения.

В выставочной документации процесс ветки объясняется по этой ссылке, процедура проста:

  1. установить пакет expo глобально, используя npm:

    npm install -g exp

  2. Внутри вашего проекта запустите код отсоединения, он создаст папки android и ios в вашем каталоге проекта:

    exp detach

Затем вы можете внести необходимые изменения в файл манифеста Android.

Ответ 2

Общая ошибка в Android. Попробуйте без использования третьего параметра:

getCurrentLocation = () =>
         navigator.geolocation.getCurrentPosition(
            (position) => {
                let currentUserPosition = position.coords;
                alert(JSON.stringify(currentUserPosition));
            },
            (error) => {
                console.log(error);
            }
        );

Итак, я просто немного вычислил код и в основном все, что делает LocationModule, отправляет запрос прямо на Android.

Он просто вызывает этот метод:

https://developer.android.com/reference/android/location/LocationManager.html#getLastKnownLocation(java.lang.String)

Теперь, если вы прочтете это, в местоположении указывается FINE_LOCATION или COARSE_LOCATION.

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

https://developer.android.com/reference/android/Manifest.permission.html#ACCESS_COARSE_LOCATION

Если нет, я бы добавил, что разрешения в приложении и повторите попытку.

Ответ 3

У меня была такая же проблема, и я попробовал много библиотек, чтобы получить местоположение пользователя. navigator.geolocation API не дал мне место каждый раз на андроиде, а просто дал только ошибку с превышением времени. Итак, я попробовал отредактировать родную геолокацию фона, которая начнет использовать gps непрерывно, тогда я нахожу реагировать на родную геолокационную службу, а автор явно отметил, что:

Поскольку эта библиотека предназначалась для замены для RN's API геолокации, использование довольно прямолинейно, с некоторыми дополнительными ошибки для обработки.

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

Посмотрите на этот файл AndroidManifest, автор использует все типы местоположений.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.agontuk.RNFusedLocation">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

для тестового примера вы просто выполните ту же инструкцию, что и предлагаемый API геолокации RN. Вот простой пример, использующий встроенную службу геолокации:

import Geolocation from 'react-native-geolocation-service';

componentDidMount() {
    // Instead of navigator.geolocation, just use Geolocation.
    if (hasLocationPermission) {
        Geolocation.getCurrentPosition(
            (position) => {
                console.log(position);
            },
            (error) => {
                // See error code charts below.
                console.log(error.code, error.message);
            },
            { enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 }
        );
    }
}

Итак, в третьем параметре getCurrentPosition используйте enableHighAccuracy false, чтобы использовать сеть, чтобы получить местоположение пользователя и true, чтобы получить более точное местоположение с помощью gps. См. эту опционную документацию.

Автор этого репо также предоставил примерный проект, вы можете попробовать его с помощью enableHighAccuracy false и отключить местоположение в первый раз, но когда вы запустите приложение, он попросит разрешения на местоположение, которое будет показывать приложение, чтобы получить доступ к Wi-Fi или сети сотовой сети.

Поскольку вы используете expo, вы не можете использовать отредактировать собственную службу геолокации, для чего вам нужно следовать Mojtaba ответьте для отстранения и получите проект android и iOS, чтобы вы могли настроить оба проекта. Дайте мне знать, если вам нужна дополнительная информация.

Ответ 4

В моем случае я решил свою проблему, добавив следующее в AndroidManifest.xml.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Ответ 5

Прежде всего, дайте разрешения в вашем приложении. Для Ios: вам нужно включить ключ NSLocationWhenInUseUsageDescription в Info.plist

Для андроида:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

Отсюда мы получаем широту и долготу КОД ==>

navigator.geolocation.getCurrentPosition(
    (position) => {
        const initialPosition = JSON.stringify(position);
        // console.log(initialPosition);
        this.setState({ initialPosition });
    },
    //(error) => alert(error.message),
    { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
);

this.watchID = navigator.geolocation.watchPosition((position) => {
    const lastPosition = JSON.stringify(position);
    //this.setState({ longitude: position.coords.longitude, latitude: position.coords.latitude });
    //this._getWheaterData();
    AsyncStorage.setItem('latitude',position.coords.latitude.toString())
    AsyncStorage.setItem('longitude',position.coords.longitude.toString())      
},
//     (error) => alert(error.message),
    //     console.log(error),
    { enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 });