世博会后台权限异步不起作用

问题描述 投票:0回答:2

我正在开发一个应用程序,有时需要跟踪我的用户,因为他们在应用程序之外。我已经浏览了博览会文档,看来我应该使用前台和后台权限异步函数,但我的博览会应用程序无法识别这两个版本。有人遇到过这种情况么?我该如何解决?以下是我的一些代码:

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 上进行了测试,并引发了相同的错误。

javascript react-native permissions background expo
2个回答
7
投票

使用

expo-location
时,必须牢记以下几点:

  1. Location.requestBackgroundPermissionsAsync()
    要求用户在应用程序处于后台时授予位置权限。在 Android 11 或更高版本上:此方法将打开系统设置页面 - 在此之前,您应该向用户解释为什么您的应用程序需要后台位置权限

  2. 在请求后台权限之前应先授予前台权限(您的应用程序在没有前台权限的情况下无法获取后台权限).

请参阅文档此处

注意:这已在 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

然后,对于后台位置权限,它会将我带到设备设置页面以允许在后台访问位置

如果万一这不能按预期工作,请尝试以下步骤

  1. 清除给予
    Expo Go
    应用程序的所有权限。
  2. 清除缓存和应用程序数据
  3. 卸载并重新启动您的设备,然后再次下载
    Expo go
    应用程序

0
投票

我使用 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()}`);
}

© www.soinside.com 2019 - 2024. All rights reserved.