我正在开发一个应用程序,有时需要跟踪我的用户,因为他们在应用程序之外。我已经浏览了博览会文档,看来我应该使用前台和后台权限异步函数,但我的博览会应用程序无法识别这两个版本。有人遇到过这种情况么?我该如何解决?以下是我的一些代码:
import * as Permissions from 'expo-permissions';
import * as Request from 'expo-permissions';
import * as Location from 'expo-location';
import Constants from 'expo-constants'
useEffect(() => {
(async () => {
if (Platform.OS === 'android' && !Constants.isDevice) {
setErrorMsg(
'Oops, this will not work on Snack in an Android emulator. Try it on your device!'
);
return;
}
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
})();
}, []);
useEffect (() =>{
(async () => {
if (Platform.OS === 'android' && !Constants.isDevice){
setErrorMsg2(
'Whoops'
);
return;
}
let { status2 } = await Location.requestBackgroundPermissionsAsync();
if (status2 !== 'granted') {
setErrorMsg2('Permission to access background location was denied');
return;
}
let location2 = await Location.getCurrentPositionAsync({})
setLocation2(location2)
})()
},[])
let text = 'Waiting..';
if (errorMsg) {
text = errorMsg;
}
else if (location) {
text = JSON.stringify(location);
}
let text2 = 'Also Wating...';
if (errorMsg2){
text2 = errorMsg2;
}
else if (location) {
text2 = JSON.stringify(location)
}
我在托管工作流程中运行 Expo,当前使用 Expo 版本 3.23.2,但也在版本 4.3.2 上进行了测试,并引发了相同的错误。
使用
expo-location
时,必须牢记以下几点:
Location.requestBackgroundPermissionsAsync()
要求用户在应用程序处于后台时授予位置权限。在 Android 11 或更高版本上:此方法将打开系统设置页面 - 在此之前,您应该向用户解释为什么您的应用程序需要后台位置权限。
在请求后台权限之前应先授予前台权限(您的应用程序在没有前台权限的情况下无法获取后台权限).
请参阅文档此处
注意:这已在 SDK 41 和
expo-location
的版本 12.2.1
上进行了测试
我是如何实现的
import { useState, useEffect } from "react";
import { Text, View, StyleSheet } from "react-native";
import * as Location from "expo-location";
export default function App() {
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") {
setErrorMsg("Permission to access location was denied");
return;
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
let backPerm = await Location.requestBackgroundPermissionsAsync();
console.log(backPerm);
})();
}, []);
let text = "Waiting..";
if (errorMsg) {
text = errorMsg;
} else if (location) {
text = JSON.stringify(location);
}
return (
<View style={styles.container}>
<Text style={styles.paragraph}>{text}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
paddingTop: 50,
backgroundColor: "#ecf0f1",
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: "bold",
textAlign: "center",
},
});
首先,它询问我前台权限,我点击了
While Using the app
然后,对于后台位置权限,它会将我带到设备设置页面以允许在后台访问位置
如果万一这不能按预期工作,请尝试以下步骤
Expo Go
应用程序的所有权限。Expo go
应用程序我使用 notifee 作为专用前台服务来进行更精细的控制,而不是来自 expo 的任务运行程序。然后我只需使用 setTimeout 创建一个简单的函数来定期运行,并使用 expo-location 来获取位置。
import * as Location from 'expo-location';
import Constants from 'expo-constants';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { startActivityAsync, ActivityAction } from 'expo-intent-launcher';
import notifee, { type Event, AndroidColor, EventType } from '@notifee/react-native';
import type { LocationObject } from 'expo-location';
export { register, unregister }
async function waitUntil(
conditionPromise: () => Promise<boolean>,
loopFunc: () => Promise<any>,
timeout: number
) {
return await new Promise(resolve => {
console.log(`Creating interval task at date: ${new Date().toISOString()}`);
const interval = setInterval(
async () => {
const condition = await conditionPromise();
if (condition) {
console.log(`Stopping interval task at date: ${new Date().toISOString()}`);
resolve('stopping');
clearInterval(interval);
return;
} else {
return loopFunc();
}
},
timeout
);
});
}
async function register(func: (locations: LocationObject[]) => Promise<void>) {
// Request permissions (required for iOS)
await notifee.requestPermission()
// Create a channel (required for Android)
const channelId = await notifee.createChannel({
id: 'default',
name: 'Default Channel',
});
notifee.registerForegroundService((notification) => {
console.log('register foreground', notification);
return new Promise(() => {
waitUntil(async () => {
const value = await AsyncStorage.getItem('@Notification_id');
//console.log(`AsyncStorage @Notification_id: ${value}`);
return !value;
}, async () => {
let locationObject;
try {
locationObject = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.Highest,
});
//console.log(`Got background fetch call at date: ${new Date().toISOString()}`);
const prevLocations = JSON.parse((await AsyncStorage.getItem('@Locations')) || '[]') as LocationObject[];
const locations = [...prevLocations, locationObject]
await func(locations); // callback to application layer
// cleanup
await AsyncStorage.removeItem('@Locations');
// update our live status of notification to indicate it is working
const notificationId = await AsyncStorage.getItem('@Notification_id');
if (notificationId) {
await notifee.displayNotification({
id: notificationId,
title: notification.title,
body: notification.body,
android: {
...notification.android,
color: AndroidColor.NAVY,
ongoing: true,
progress: {
indeterminate: true,
},
pressAction: {
id: 'stop',
},
},
});
}
} catch (e) {
//console.log('Inside try catch', e);
if (e instanceof Error) {
const notificationId = await AsyncStorage.getItem('@Notification_id');
// update our foreground notification to indicate error
if (notificationId) {
await notifee.displayNotification({
id: notificationId,
title: notification.title,
body: e.message,
android: {
...notification.android,
color: AndroidColor.OLIVE,
ongoing: true,
progress: undefined,
pressAction: {
id: 'error',
},
}
});
}
if (/network request failed/i.test(e.message)) {
// save
await AsyncStorage.setItem('@Locations', JSON.stringify(locations));
}
}
}
}, 30000).catch(console.log)
const eventCallback = async ({ type, detail }: Event) => {
if (type === EventType.ACTION_PRESS || type === EventType.PRESS) {
if (detail?.pressAction?.id === 'stop') {
await notifee.stopForegroundService();
await AsyncStorage.removeItem('@Notification_id');
} else if (detail?.pressAction?.id === 'error') {
// Go to location sources settings page, regardless of error
await startActivityAsync(ActivityAction.LOCATION_SOURCE_SETTINGS);
}
}
}
notifee.onForegroundEvent(async ({ type, detail }) => {
//console.log(`onForegroundEvent`, type, JSON.stringify(detail, null, 4));
await eventCallback({ type, detail }).catch(console.log);
});
notifee.onBackgroundEvent(async ({ type, detail }) => {
//console.log(`onBackgroundEvent`, type, JSON.stringify(detail, null, 4));
await eventCallback({ type, detail });
});
});
});
const notificationId = await notifee.displayNotification({
title: 'App',
body: 'Tracking your location in the background. Click notification to disable.',
android: {
channelId,
asForegroundService: true,
color: AndroidColor.NAVY,
ongoing: true,
colorized: true,
pressAction: {
id: 'stop',
},
},
});
await AsyncStorage.setItem('@Notification_id', notificationId)
console.log(`Registered notification at date: ${new Date().toISOString()}`);
return notificationId;
}
async function unregister(notificationId?: string) {
await notifee.stopForegroundService();
notificationId && await notifee.cancelNotification(notificationId);
await AsyncStorage.removeItem('@Notification_id');
console.log(`Unregister notification at date: ${new Date().toISOString()}`);
}