transistorsoft / react-native-background-geolocation

Sophisticated, battery-conscious background-geolocation with motion-detection
http://shop.transistorsoft.com/pages/react-native-background-geolocation
MIT License
2.64k stars 425 forks source link

HttpService (Deobfuscated class name) com.transistorsoft.locationmanager.http.HttpService.stopMonitoringConnectivityChanges #955

Closed UnSket closed 4 years ago

UnSket commented 4 years ago

Your Environment

Expected Behavior

App will be running

Actual Behavior

App crushes

Steps to Reproduce

  1. Launch app, geolocation on
  2. Close app
  3. Go to geolocation settings
  4. Fast change geolocation to wifi and mobile network and launch app

Debug logs

189020231397890.txt

Logs ![image](https://user-images.githubusercontent.com/31548789/72620321-1fafc000-3950-11ea-9bbb-e40399e8f016.png)
christocracy commented 4 years ago

Please provide properly formatted logs

UnSket commented 4 years ago

Here it is. Sorry 189020231397890 (1).txt

christocracy commented 4 years ago

Show some off your code. You're doing something weird.

UnSket commented 4 years ago

Here is component, which responsible for almost all work with plugin

import BackgroundGeolocation from 'react-native-background-geolocation';
import { NativeModules } from 'react-native';
import { connect } from 'react-redux';
import React from 'react';
import pkg from '../../../package.json';
import { endpoints } from '../../sagas/api';
import {
  geolocationUpdated,
  geolocationError,
  geolocationSent,
  geolocationSendError,
  geolocationEvent,
  geolocationClearPositions,
} from '../../actions/geolocation';
import _ from 'lodash';
import { createSelector } from 'reselect';

class Geolocation extends React.Component {
  render() {
    return null;
  }

  async componentDidMount() {
    const { dispatch, options } = this.props;

    // Unsubscribe from old events
    await BackgroundGeolocation.removeListeners();

    // Subscribe to `location` event
    // -------------------
    BackgroundGeolocation.onLocation(
      location => dispatch(geolocationUpdated(location.coords)), // (!) use 'location.coords', not 'location'
      error => dispatch(geolocationError(error)),
    );

    // Post events to AppMetrica for debugging
    // ---------------------------------------
    BackgroundGeolocation.onHttp(http => {
      if (http.success) {
        dispatch(geolocationSent(http));
      } else {
        dispatch(geolocationSendError(http));
        // clear locations if have bad data
        if (http.status === 422) {
          BackgroundGeolocation.destroyLocations();
          dispatch(geolocationClearPositions());
        }
      }
    });

    // event = { isMoving: true, location } - Device just started moving
    // event = { isMoving: false, location } - Device just stopped
    BackgroundGeolocation.onMotionChange(event => {
      dispatch(geolocationEvent('motionchange', event));
    });

    // Fires every 60 seconds
    BackgroundGeolocation.onHeartbeat(event => {
      dispatch(geolocationEvent('heartbeat', event));
    });

    // Fires when internet connection becomes available / not available
    BackgroundGeolocation.onConnectivityChange(event => {
      dispatch(geolocationEvent('connectivitychange', event));
    });

    // event = { activity: 'still'|'on_foot'|'in_vehicle'|'on_bicycle'|'running', confidence: 100% }
    BackgroundGeolocation.onActivityChange(event => {
      dispatch(geolocationEvent('activitychange', event));
    });

    dispatch(geolocationEvent('1. Reset config. Initialize with new config', options));
    await BackgroundGeolocation.reset(options);

    try {
      dispatch(geolocationEvent('2. Initialized config. Starting service'));
      await BackgroundGeolocation.start();

      dispatch(geolocationEvent('3. Started service. Setting pace to moving'));
      await BackgroundGeolocation.changePace(true);

      dispatch(geolocationEvent('4. Set pace to moving. Asking current location once'));
      const location = await BackgroundGeolocation.getCurrentPosition({
        maximumAge: 60 * 1000, // 1 minute
        desiredAccuracy: 100, // 100 meters
        samples: 1, // override default 3 samples (to choose most accurate) before firing location
        persist: true, // save in SQLite (will POST to /push-positions)
      });

      dispatch(geolocationEvent('5. Got first location'));
      dispatch(geolocationUpdated(location.coords));
    } catch (error) {
      dispatch(geolocationError(error));
    }
  }

  shouldComponentUpdate(nextProps) {
    return !_.isEqual(this.props.options, nextProps.options); // deep compare
  }

  async componentDidUpdate(prevProps) {
    const { options, dispatch } = this.props;
    // Sync component state with plugin state
    dispatch(geolocationEvent('Pushing config update to plugin', options));
    await BackgroundGeolocation.setConfig(options);

    const isStarted = !prevProps.options.startOnBoot && options.startOnBoot;
    if (isStarted) {
      await BackgroundGeolocation.destroyLocations();
      dispatch(geolocationClearPositions());
      NativeModules.PinnerModule.startService(true);
    }

    const isStopped = !options.startOnBoot && prevProps.options.startOnBoot;
    if (isStopped) {
      NativeModules.PinnerModule.stopService();
    }
  }
}

const getSettings = state => state.settings;
const getToken = state => state.api.token;
const getRoute = state => state.routes.byID[state.routes.selected];
const getCourier = state => state.app.courier;
const getAPIVersion = state => state.api.version;

const getOptions = (settings, token, route, courier, apiVersion) => {
  // token not null = phone is authorized
  // courier not null = courier logged in
  // route not null = courier has orders today
  const enableTracking = !!(token && route && courier); // must be boolean
  let endpoint = 'push-positions';
  let pushPositionsV2Settings = {};

  if (settings.usePushPositionsV2) {
    pushPositionsV2Settings = {
      enableTimestampMeta: true,
      locationTemplate: '',
      maxBatchSize: 10,
    };
    endpoint = 'push-positions-v2';
  }

  return {
    desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH, // Use GPS, WiFi and Cellular for geolocation
    distanceFilter: 10, // meters to move before update event fires
    fastestLocationUpdateInterval: 1000, // if another app is using location, accept updates every N ms
    locationUpdateInterval: 10000, // actively request location every N ms
    heartbeatInterval: 60, // Send 'heartbeat' events every 60 seconds
    debug: __DEV__, // for debug sounds
    logLevel: __DEV__
      ? BackgroundGeolocation.LOG_LEVEL_VERBOSE
      : BackgroundGeolocation.LOG_LEVEL_DEBUG,
    stopOnTerminate: !enableTracking, // Stop plugin when app is closed and tracking is OFF (e.g. when logged out)
    startOnBoot: enableTracking, // Start on Android boot if tracking is ON
    foregroundService: true, // show in notification bar, keep service alive as much as possible
    notificationTitle: 'Яндекс.Курьер',
    notificationText: 'Использует GPS',
    maxDaysToPersist: 3,
    url: enableTracking
      ? `${endpoints[apiVersion]}/couriers/${courier.id}/routes/${route.id}/${endpoint}`
      : null,
    autoSync: true,
    batchSync: true,
    maxBatchSize: 100, // Max locations size in request
    httpRootProperty: 'positions',
    locationTemplate:
      '{ "accuracy": <%= accuracy %>, "latitude":<%= latitude %>, "longitude":<%= longitude %>, "time": "<%= timestamp %>" }',
    headers: {
      Authorization: `Auth ${token}`,
      'User-Agent': `com.yandex.courier v${pkg.version}`,
    },
    enableHeadless: true,
    ...settings.geolocation, // settings to override these values
    ...pushPositionsV2Settings,
  };
};

const optionsSelector = createSelector(
  [getSettings, getToken, getRoute, getCourier, getAPIVersion],
  getOptions,
);

export default connect(state => ({
  options: optionsSelector(state),
}))(Geolocation);
christocracy commented 4 years ago

FYI: enable multi-line syntax highlighting in markdown by wrapping code in 3 backticks, *not one**.

christocracy commented 4 years ago

This looks really weird. Where are you calling #ready?

What's the point of this?

  stopOnTerminate: !enableTracking, // Stop plugin when app is closed and tracking is OFF (e.g. when logged out)
    startOnBoot: enableTracking, 

When you want to stop tracking, just call #stop. There's no point modifying these params.

UnSket commented 4 years ago

When a user enters the application, he is not authorized, but I want the location to be determined and displayed on the map. To do this, I run the plugin, but I set it the flag of enableTracking: false. So, if the user does not log in and exits or minimizes the application, the plugin will not work as ia m expected. When the user logs in, I set the flag true.

I do not call the ready method. Instead, I call reset and then start

if i call stop current location will not be determined

christocracy commented 4 years ago

Don’t do that, it’s ugly.

Use the method getCurrentPosition. If you want that position persisted while plugin disabled, explicitly set persist: true in the options to that method.

Read the api docs for getCurrentPosition.

UnSket commented 4 years ago

Ok, but will it fix the problem?

christocracy commented 4 years ago

I do not call the read method. Instead, I call reset and then start

You MUST call ready. If you don’t, bad things will happen. Especially with iOS, which won’t even work.

UnSket commented 4 years ago

Thank you! it seems to have helped