Экспо отслеживание на переднем плане останавливается на Android при выключении экрана - отслеживание тропы неожиданно осAndroid

Форум для тех, кто программирует под Android
Ответить Пред. темаСлед. тема
Anonymous
 Экспо отслеживание на переднем плане останавливается на Android при выключении экрана - отслеживание тропы неожиданно ос

Сообщение Anonymous »

Я использую Expo + React Native с помощью Expo-Location и Expo-Task-Manager, чтобы реализовать систему отслеживания местоположения переднего плана для отслеживания трасс для хлебной крошки, в то время как пользователь перспектирует в поле. Однако, когда экран выключен или приложение на некоторое время находится в фоновом режиме, отслеживание неожиданно останавливается, и когда пользователь возвращается в приложение, трасса для хлебной крошки больше не записывается.

Код: Выделить всё

import { useEffect, useRef, useState } from 'react';
import { showNativeToast } from '@/utils/useNativeToast';
import { useMapContext } from '@/context/MapContext';
import useToast from '@/hooks/useToast';
import { Platform } from 'react-native';
import * as Location from 'expo-location';
import * as Notifications from 'expo-notifications';
import { BreadcrumbFeature } from '@/types/breadcrumbTypes';
import { TRAIL_COLORS } from '@/constants/colors';
import * as TaskManager from 'expo-task-manager';
import { checkBatteryOptimization } from '@/utils/batteryOptimization';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { ActivityType } from 'expo-location';
import { StorageKeys } from '@/constants/storageKeys';
import { calculateDistance } from '@/utils/calculateDistance';

const LOCATION_TASK_NAME = 'TrailTracking';

export const MIN_DISTANCE_METERS = 0;
const MAX_ACCURACY_METERS = 30;
const MAX_DISTANCE_JUMP_METERS = 50;

interface ReturnUseBreadcrumbTrail {
startTracking: () => Promise;
stopTracking: () => Promise;
discardPath: () => void;
setTrailName: (name: string) => void;
setTrailColor: (color: string) => void;
trailProperties: { name: string; color?: string };
addLocationPoint: (
coords:
| {
latitude: number;
longitude: number;
accuracy?: number;
speed?: number;
}
| {
latitude: number;
longitude: number;
accuracy?: number;
speed?: number;
}[],
) => void;
getCurrentBreadcrumb: () => BreadcrumbFeature;
}

const foregroundService = {
notificationTitle: 'Trail Tracking',
notificationBody: 'Your location is being tracked.',
channelId: 'location-tracking',
};

const locationConfig = {
accuracy: Location.Accuracy.BestForNavigation,
timeInterval: 0,
distanceInterval: 0,
foregroundService: Platform.OS === 'android' ? foregroundService : undefined,
pausesUpdatesAutomatically: false,
activityType: ActivityType.OtherNavigation,
mayRequireBackgroundAccess: true,
...(Platform.OS === 'ios' && {
allowsBackgroundLocationUpdates: true,
showsBackgroundLocationIndicator: true,
}),
};

export function useBreadcrumbTrail(): ReturnUseBreadcrumbTrail {
const { setIsBreadcrumbTracking, setLiveBreadcrumb, liveBreadcrumb } =
useMapContext();
const { showSuccessToast, showErrorToast } = useToast();
const [trailProperties, setTrailProperties] = useState({
name: '',
color: TRAIL_COLORS[0].color,
});
const isTracking = useRef(false);

const addLocationPoint = async (
coords:
| {
latitude: number;
longitude: number;
accuracy?: number;
speed?: number;
}
| {
latitude: number;
longitude: number;
accuracy?: number;
speed?: number;
}[],
) => {
setLiveBreadcrumb(prev => {
const coordinates = [...prev.geometry.coordinates];
const points = Array.isArray(coords) ? coords : [coords];

points.forEach(p => {
const isDriving = p.speed !== undefined && p.speed > 3.6; // ~10km/h

if (!isDriving && p.accuracy !== undefined && p.accuracy > MAX_ACCURACY_METERS) {
return;
}

const lastCoord =
coordinates.length > 0 ? coordinates[coordinates.length - 1] : null;

if (!lastCoord) {
coordinates.push([p.longitude, p.latitude]);
return;
}

const lastPoint = { latitude: lastCoord[1], longitude: lastCoord[0] };
const dist = calculateDistance(lastPoint, p);

if (isDriving || dist   MIN_DISTANCE_METERS) {
coordinates.push([p.longitude, p.latitude]);
}
}
});
const updatedBreadcrumb = {
...prev,
geometry: {
...prev.geometry,
coordinates,
},
};
AsyncStorage.setItem(
StorageKeys.CURRENT_BREADCRUMB,
JSON.stringify(updatedBreadcrumb),
);
return updatedBreadcrumb;
});

try {
await AsyncStorage.removeItem('breadcrumb_points');
} catch (e) {
console.error('Failed to clear persisted breadcrumb points:', e);
}
};

useEffect(() => {
(async () => {
try {
const stored = await AsyncStorage.getItem('breadcrumb_points');
if (stored) {
const points = JSON.parse(stored);
if (points && points.length > 0) {
await addLocationPoint(points);
}
}
} catch (e) {
console.error('Failed to load persisted breadcrumb points:', e);
}
})();
return () => {
global.addLocationPoint = null;
};
}, []);

const startTracking = async (): Promise => {
try {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
showErrorToast('Permission to access location was denied');
return false;
}

if (Platform.OS === 'android') {
const isOptimized = await checkBatteryOptimization();
if (!isOptimized) {
return false;
}
}

const { granted } = await Notifications.requestPermissionsAsync({
ios: {
allowAlert: true,
allowBadge: true,
allowSound: true,
},
});

if (!granted) {
showErrorToast('Permission to display notification was denied');
return false;
}
if (Platform.OS === 'ios') {
const backgroundStatus =
await Location.requestBackgroundPermissionsAsync();

if (backgroundStatus.status !== 'granted') {
showErrorToast('Background location permission is required');
return false;
}
}

const isTaskRegistered =
await TaskManager.isTaskRegisteredAsync(LOCATION_TASK_NAME);
if (isTaskRegistered) {
await Location.stopLocationUpdatesAsync(LOCATION_TASK_NAME);
}

global.addLocationPoint = addLocationPoint;

await Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, {
...locationConfig,
...(Platform.OS === 'android' && { foregroundService }),
});

isTracking.current = true;
setIsBreadcrumbTracking(true);
showSuccessToast('Tracking started');
return true;
} catch (error) {
console.warn('Error starting tracking:', error);
showNativeToast(
'Failed to start tracking.  Please ensure location services are enabled and permissions are granted.',
);
return false;
}
};

const setTrailName = (name: string) => {
setTrailProperties(prev => ({ ...prev, name }));
setLiveBreadcrumb(prev => ({
...prev,
properties: { ...prev.properties, name },
}));
};

const setTrailColor = (color: string) => {
setTrailProperties(prev => ({ ...prev, color }));
setLiveBreadcrumb(prev => ({
...prev,
properties: { ...prev.properties, color },
}));
};

const stopTracking = async () => {
try {
const isTaskRegistered =
await TaskManager.isTaskRegisteredAsync(LOCATION_TASK_NAME);
if (isTaskRegistered) {
await Location.stopLocationUpdatesAsync(LOCATION_TASK_NAME);
}
isTracking.current = false;
setIsBreadcrumbTracking(false);
global.addLocationPoint = null;
} catch (error) {
console.warn('Error stopping tracking:', error);
isTracking.current = false;
setIsBreadcrumbTracking(false);
global.addLocationPoint = null;
}
};

const discardPath = () => {
if (isTracking.current) {
stopTracking();
}

setLiveBreadcrumb({
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [],
},
properties: {
name: '',
color: TRAIL_COLORS[0].color,
createdAt: Date.now(),
},
});
setTrailProperties({ name: '', color: TRAIL_COLORS[0].color });
};

const getCurrentBreadcrumb = (): BreadcrumbFeature => {
return {
...liveBreadcrumb,
properties: {
...liveBreadcrumb.properties,
color: trailProperties.color || TRAIL_COLORS[0].color,
createdAt: liveBreadcrumb.properties.createdAt || Date.now(),
},
};
};

useEffect(() => {
return () => {
if (isTracking.current) {
stopTracking();
}
};
}, []);

return {
startTracking,
stopTracking,
discardPath,
setTrailName,
setTrailColor,
trailProperties,
addLocationPoint,
getCurrentBreadcrumb,
};
}
Запрашивая разрешения на местоположение фонового местоположения (на iOS), но с использованием только отслеживания местоположения на переднем плане
предоставляется уведомления
Оптимизация батареи проверяется, и пользователи предлагаются, чтобы отключить его
Оптимирование, если

Определение. /> global.addlocationPoint () вызывается из defanetask (), чтобы добавить новые точки < /p>
Проблема < /strong>
на Android, если экран отключен или приложение на фона на некоторое время, отслеживание иногда молча останавливается < /p>
icon следом /> Похоже, что приложение, возможно, было убито или перезапущено, но нет никакого журнала аварии < /p>
ios работает, как и ожидалось - нет проблем < /p>

Подробнее здесь: https://stackoverflow.com/questions/796 ... f-trail-tr
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Android»