首页 > 解决方案 > 如何提高 React Native 地理定位运行屏幕的性能

问题描述

嗨,我目前正在尝试构建一个类似于在 React Native 中运行的 UnderArmor 运行应用程序的运行应用程序。这些功能有效,但在模拟器上使用应用程序几分钟后,计算机开始过热,应用程序的性能显着下降,一段时间后不再响应。我是否每帧进行了太多计算?感谢您的帮助

const { width, height } = Dimensions.get('window')
const LATITUDE_DELTA = 0.007;
const LONGITUDE_DELTA = 0.007;
const LATITUDE = 37.78825;
const LONGITUDE = -122.4324;
var email = "";

const RunMapScreen = () => {
  const paperTheme = useTheme();
  const [state, setState] = useState({
    isActive: false,
    close: true,
    routeCoordinates: [],
    distanceTravelled: 0,
    prevLatLng: {},
    latitude: LATITUDE,
    longitude: LONGITUDE,
    seconds: 0,
    now: moment(),
    then: moment(),
    timeElapsed: "00:00:00",
    startCounter: 0,
    speedCounter: 1,
    speed: 0,
    averageSpeed: 0,
    isModalVisible: false,
    email: "grt",
  });
  const [isModalVisible, setModalVisible] = useState(false);

  useEffect(() => {
    this.watchID = navigator.geolocation.watchPosition((position) => {

      handleUpdates(position, position.coords.latitude, position.coords.longitude, position.coords.speed);
    });

    return () => {
      navigator.geolocation.clearWatch(this.watchID);
    }
  }, [state]);

  const handleUpdates = (position, lat, long, speedVal) => {
    const newLatLngs = { latitude: position.coords.latitude, longitude: position.coords.longitude }
    const positionLatLngs = _.pick(position.coords, ['latitude', 'longitude']);
    setState(state => ({ ...state, latitude: lat, longitude: long }));
    if (state.isActive) {
      setState(state => ({
        ...state,
        routeCoordinates: state.routeCoordinates.concat(positionLatLngs),
        distanceTravelled: state.distanceTravelled + calcDistance(newLatLngs),
        prevLatLng: newLatLngs,
        now: moment(),
        timeElapsed: moment.utc(moment(state.now, "DD/MM/YYYY HH:mm:ss").diff(moment(state.then, "DD/MM/YYYY HH:mm:ss"))).format("HH:mm:ss"),
        speedCounter: state.speedCounter + 1,
        speed: speedVal,
        averageSpeed: ((state.averageSpeed * (state.speedCounter - 1) + state.speed) / state.speedCounter),
      }));
    }
  };

  const calcDistance = (newLatLng) => {
    const prevLatLng = state.prevLatLng;
    return (haversine(prevLatLng, newLatLng) || 0);
  };

  const openIsActive = () => {
    var now;
    if (!state.isActive && state.startCounter === 0) {
      setState(state => ({
        ...state,
        timeElapsed: moment.duration(state.now.diff(state.then)),
        then: moment(),
        startCounter: 1
      }));
    } else if (state.isActive && state.startCounter === 1) {
      now = { ...state.now };
    } else if (!state.isActive && state.startCounter === 1) {
      var then = { ...state.then };
      var diff = -state.now.diff(now);
      setState(state => ({ ...state, then: moment(then).add(diff) }));
    }
    setState(state => ({ ...state, isActive: !state.isActive }));
  }

  const saveData = () => {
    firebase.auth().onAuthStateChanged((user) => {
      var ref = firebase.database().ref(user.email.replace('.', ''));
      var key = firebase.database().ref(ref).push().key;
      firebase.database().ref(ref).child(key).set({
        email: user.email,
        distance: state.distanceTravelled,
        time: state.timeElapsed,
        speed: state.speed,
        averageSpeed: state.averageSpeed
      });
    });

    setState(state => ({
      ...state,
      isActive: false,
      routeCoordinates: [],
      distanceTravelled: 0,
      prevLatLng: {},
      seconds: 0,
      now: moment(),
      then: moment(),
      timeElapsed: "00:00:00",
      startCounter: 0,
      speedCounter: 1,
      speed: 0,
      averageSpeed: 0,
    }));
    navigator.geolocation.clearWatch(watchID);
    }));
  }

  const endRun = () => {
    if (state.isActive) {
      setState(state => ({ ...state, isActive: false }));
      Alert.alert(
        "End Run",
        "Do you want to end the run?",
        [{
          text: "Cancel",
          onPress: () => { setState(state => ({ ...state, isActive: true })) },
          style: "cancel"
        },
        {
          text: "OK",
          onPress: () => saveData()
        }], { cancelable: false });
    } else {
      Alert.alert(
        "Error",
        "You cannot end run that hasn't started",
        [{
          text: "OK",
          style: "cancel"
        }], { cancelable: false });
    }
  }

  const toggleModal = () => {
    setState(state => ({ ...state, isModalVisible: !state.isModalVisible }));
  };

  useEffect(() => {
    GetLocation.getCurrentPosition({
      enableHighAccuracy: true,
      timeout: 15000,
    })
      .then(pos => {
        setState(state => ({ ...state, latitude: pos.latitude, longitude: pos.longitude }));
      })
      .catch(error => {
        const { code, message } = error;
        console.log(code, message);
      });
  });

  const getMapRegion = () => ({
    latitude: state.latitude,
    longitude: state.longitude,
    latitudeDelta: LATITUDE_DELTA,
    longitudeDelta: LONGITUDE_DELTA
  });

  const { colors } = useTheme();

  const theme = useTheme();

  return (
    <View style={styles.container}>
      <Map
        routeCoordinates={state.routeCoordinates}
        getRegion={getMapRegion()} />

      <Button title="Show modal" onPress={toggleModal} />

      <Modal testID={'modal'}
        isVisible={state.isModalVisible}
        onSwipeComplete={() => { setModalVisible(false) }}
        swipeDirection={['up', 'left', 'right', 'down']}
        style={styles.modalView}>
        <ModalView
          timeElapsed={state.timeElapsed}
          distanceTravelled={state.distanceTravelled}
          speed={state.speed}
          averageSpeed={state.averageSpeed}
          toggleModal={toggleModal} />
      </Modal>
      <FloatingButton
        openIsActive={openIsActive}
        endRun={endRun}
        toggleModal={toggleModal}
        style={{ bottom: 100 }} />
    </View>
  );
};

export default RunMapScreen;

地图组件

const { width, height } = Dimensions.get('window')

export const Map = memo(props => {
  const paperTheme = useTheme();

  var mapTheme = 'standard';
  if (paperTheme.dark === true) {
    mapTheme = 'hybrid';
  }
  var backGroundColor = '#404040';

  return (
    <MapView
      provider="google"
      style={styles.map}
      mapType='standard'
      showsUserLocation={true}
      followUserLocation={true}
      region={props.getRegion}
      tintColor='#404040'
      overlays={[{
        coordinates: props.routeCoordinates,
        strokeColor: '#F02A4B',
        lineWidth: 10,
      }]}
    >
      <Polyline
        coordinates={props.routeCoordinates}
        strokeColor='#F02A4B'
        strokeWidth={8}
      />
    </MapView>
  );
});

标签: javascriptreactjstypescriptperformancereact-native

解决方案


主要问题可能是在useEffect,您开始观察位置。您使用的第二个参数是[state]. 在里面useEffect,你也更新了state. 这意味着无论何时[state]更改,您useEffect都会再次运行!这意味着无限循环,您可能会在短短几分钟内吸引数百名听众到该位置。要解决此问题,您可能希望传递一个空数组作为第二个参数。在React hooks 文档中阅读有关第二个参数的更多信息

另一个小错误:您设置this.watchIdwatchId在清理功能中使用。您可能希望将行更改this.watchid为:

this.watchID = navigator.geolocation.watchPosition((position) => {

推荐阅读