首页 > 解决方案 > Expo Location问题:watchPositionAsync不返回值或长时间(甚至10分钟)返回

问题描述

晚上好,我正在为业余爱好开发我的第一个 React Native Expo 应用程序。这是一个使用手机地理定位来跟踪用户移动的应用程序,以便在城市中组织寻宝活动。但是我遇到了一个问题:watchPositionAsync 函数的工作非常不稳定。有时位置对象的值会立即返回。但大多数时候,它根本不会被退回,甚至需要 10 分钟才能完成。

这是代码的第一个版本,仅包含有问题的函数:

import React, {useState, useEffect} from 'react';
import * as Location from 'expo-location';

const Navigator = () => {

 const [deviceLocation, setDeviceLocation] = useState(null);
 const [errorMsg, setErrorMsg] = useState(null);

 const getLocationAsync = async() => {
 let loc = await Location.watchPositionAsync({
      accuracy: Location.Accuracy.BestForNavigation,
      timeInterval: 10000,
      distanceInterval : 20
    }, 
      (newLocation) => {
        setDeviceLocation(newLocation); 
      }
    );
  
  };

 getLocationAsync()

  useEffect(() => {
    (async () => {
      let { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== 'granted') {
        setErrorMsg('Permission to access location was denied');
        return;
      }
    })();
  }, []);

  let text = 'Waiting..';
  if (errorMsg) {
    text = errorMsg;
  } else if (deviceLocation.location !== '') {
    text = JSON.stringify(deviceLocation.location);
  }
    
   return (
       <View>
            <Text>{text}</Text>
        </View>    
    )
}

这是具有附加功能的版本(例如在每次重新渲染 watchPositionAsync 函数时计算与预定点的距离,并设置要传达给用户的特定句子):

import React, {useState, useEffect} from 'react';
import NavigatorUI from './NavigatorUI';
import * as Location from 'expo-location';
import { getDistance, findNearest } from 'geolib';


const treasureLocation = {
  latitude: 39.2695552,
  longitude: 8.4679172
}



const Navigator = () => {


  const [deviceLocation, setDeviceLocation] = useState({
    location : '',
    treasureDist : '',
    sentence: ''
  });
  const [errorMsg, setErrorMsg] = useState(null);

  const getLocationAsync = async() => {
  let loc = await Location.watchPositionAsync({
      accuracy: Location.Accuracy.BestForNavigation,
      timeInterval: 10000,
      distanceInterval : 20
    }, 
      (newLocation) => {
        setDeviceLocation({...deviceLocation, location: newLocation}); 
        getDistfromTreasure();
        switchDist();
        console.warn(deviceLocation.sentence) 
      }
    );
  
  };

  getLocationAsync()


  const getDistfromTreasure = () => {
    if(deviceLocation.location){
      let {latitude: deviceLat, longitude: deviceLong} = deviceLocation.location.coords;
      let {latitude: treasureLat, longitude: treasureLong} = treasureLocation;
    
        let dist = getDistance({
                                  latitude: deviceLat, 
                                  longitude: deviceLong 
                                }, 
                                {
                                  latitude: treasureLat, 
                                  longitude: treasureLong
                                } 
                                );
        
        setDeviceLocation({...deviceLocation, treasureDist: dist})   

    }

    };



  const switchDist = () =>{
    if(deviceLocation.treasureDist){
      let {treasureDist: val} = deviceLocation;
      if(val > 1000){
        setDeviceLocation({...deviceLocation, sentence: 'Example1'});
      }
      else if(val < 1000 && val > 500){
        setDeviceLocation({...deviceLocation, sentence: 'Example2'});
      }
      else if(val < 500 && val > 100){
        setDeviceLocation({...deviceLocation, sentence: 'Example3'});
      }
      else if(val < 100 && val > 50){
        setDeviceLocation({...deviceLocation, sentence: 'Example4'});
      }
      else if(val < 50){
        setDeviceLocation({...deviceLocation, sentence: 'Example5'});
      }

    }
    
  }


  useEffect(() => {
    (async () => {
      let { status } = await Location.requestForegroundPermissionsAsync();
      if (status !== 'granted') {
        setErrorMsg('Permission to access location was denied');
        return;
      }
    })();
  }, []);

  let text = 'Waiting..';
  if (errorMsg) {
    text = errorMsg;
  } else if (deviceLocation.location !== '') {
    text = JSON.stringify(deviceLocation.location);
  }
    

    return (
           <>
             <View>
               <Text>{text}</Text>
             </View>

             <View>
               <Text>{sentence}</Text>
             </View>
        </>
    )
       
}

export default Navigator

在这两种情况下,屏幕都会无限期地显示“等待”这个词。另外,在第二种情况下,我注意到重新渲染的发生速度比我使用 timeInterval 选项设置的快得多或慢得多。在任何情况下都具有不连续的速度。

有没有人遇到同样的问题并设法解决它?

标签: react-nativegeolocationlocationexpo

解决方案


代码对我来说似乎几乎没问题。我认为在您发布的第一个代码中,如果在渲染之前,最后一个 else 中存在错误。expo 给出的位置对象包含一个属性 coords,如果在读取之前定义了 deviceLocation,您需要检查它。

  else if (deviceLocation.location !== '') {
text = JSON.stringify(deviceLocation.location);}

->

 else if (deviceLocation && deviceLocation.coords) {
 text = JSON.stringify(deviceLocation.coords)};

我也玩了一点代码,我会把它写成:

const Navigator = () => {
  console.log('render')

  const [permissionResult, setPermissionResult] = useState(undefined);
  const [deviceLocation, setDeviceLocation] = useState(null);
  const [errorMsg, setErrorMsg] = useState(null);

  const getPermissionAsync = async () => {
    let { status } = await Location.requestForegroundPermissionsAsync();
    setPermissionResult(status)
    if (status !== 'granted') {
      setErrorMsg('Permission to access location was denied');
      return;
    }
  }

  const getLocationAsync = async() => {
    await Location.watchPositionAsync({
      accuracy: Location.Accuracy.BestForNavigation,
      timeInterval: 10000,
      distanceInterval : 20
    }, 
      (newLocation) => {
        setDeviceLocation(newLocation); 
      }
    );
  };

  useEffect(() => {
    // If permission request is not done
    if (permissionResult === undefined) {
      getPermissionAsync();
    }
  }, [permissionResult]);

  useEffect(()=>{
    // If permission has been done and the result is available
    if (permissionResult !== undefined) {
      getLocationAsync()
    }
  }, [permissionResult])

  let text = 'Waiting..';
  if (errorMsg) {
    console.log('errore')
    text = errorMsg;
  } else if (deviceLocation && deviceLocation.coords) {
    text = JSON.stringify(deviceLocation.coords);
    console.log(deviceLocation)
  }
   
  return (
      <View>
          <Text>{text}</Text>
      </View>    
  )
}

我希望这会有所帮助,并且我理解这个问题


推荐阅读