将前台服务与 Expo 和 React Native 结合使用

Ale*_*Pop 7 background-service android-permissions foreground-service react-native expo

我正在使用 React Native 和 Expo 来构建一个用于跟踪锻炼的应用程序。我想为正在进行的锻炼实现前台服务,但也想将 Expo 保留在我的项目中。

我尝试使用这种方法:https://github.com/cristianoccazinsp/react-native-foreground-service

为此,需要在 Android Manifest 文件中添加权限,为此,我在 app.json 文件中添加了权限:

"android": {
  "adaptiveIcon": {
    "foregroundImage": "./assets/adaptive-icon.png",
    "backgroundColor": "#FFFFFF",
    "permissions": ["FOREGROUND_SERVICE", "WAKE_LOCK"]
  }
},
Run Code Online (Sandbox Code Playgroud)

下一步是将服务添加到 Android Manifest 文件中:

<meta-data android:name="com.zinspector.foregroundservice.notification_channel_name"
            android:value="zInspector Service"/>
<meta-data  android:name="com.zinspector.foregroundservice.notification_channel_description"
            android:value="zInspector Service."/>
<meta-data  android:name="com.zinspector.foregroundservice.notification_color"
            android:resource="@color/orange"/>

<service android:name="com.zinspector.foregroundservice.ForegroundService"></service>
<service android:name="com.zinspector.foregroundservice.ForegroundServiceTask"></service>
```
Run Code Online (Sandbox Code Playgroud)

问题是我没有找到使用 expo 并使用 app.json 添加这些服务属性的解决方案

任何人都可以帮助我解决这个问题或提供另一个解决方案来拥有前台服务并继续使用 Expo 吗?谢谢

k2r*_*rks 7

看起来您需要创建一个expo 配置插件来将元数据和服务值添加到 AndroidManifest.xml,还需要复制一个文件。

我将使用提到的https://github.com/cristianoccazinsp/react-native-foreground-service库安装步骤作为示例。

首先,您需要开始在项目中使用expo prebuild,它会公开本机文件而不从 expo 中弹出,并让我们保留所有的 expo 工具,您可以忽略存储库中的本机文件夹,因为它们将在每个构建。

我通常会在 package.json 脚本中添加类似这样的内容,以便于访问:

"eas:build": "npx expo prebuild && eas build --platform=android",
"prebuild": "npx expo prebuild",
Run Code Online (Sandbox Code Playgroud)

您也可以通过运行npx expo prebuild并检查是否创建了本机 ios 和 android 文件夹来单独测试预构建。

接下来,让我们configPlugins在项目根目录中为我们需要的文件创建一个文件夹。

接下来,我们需要创建一个 expo 配置插件,它允许我们在 expo 预构建步骤中编辑和复制本机文件。

configPlugins/withForegroundService.ts

import { AndroidConfig, ConfigPlugin, withAndroidManifest } from '@expo/config-plugins'
import path from 'path'
import { Paths } from '@expo/config-plugins/build/android'
import fs from 'fs'

export const withForegroundService: ConfigPlugin = (config) => {
    config = withAndroidManifest(config, async (config) => {
        const mainApplication = AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults)

        // set the metadata
        AndroidConfig.Manifest.addMetaDataItemToMainApplication(
            mainApplication,
            'com.zinspector.foregroundservice.notification_channel_name',
            'zInspector Service',
            'value',
        )
        AndroidConfig.Manifest.addMetaDataItemToMainApplication(
            mainApplication,
            'com.zinspector.foregroundservice.notification_channel_description',
            'zInspector Service.',
            'value',
        )
        AndroidConfig.Manifest.addMetaDataItemToMainApplication(
            mainApplication,
            'com.zinspector.foregroundservice.notification_color',
            '@color/orange',
            'resource',
        )

        // set the services
        mainApplication.service = []
        mainApplication.service.push({
            $: {
                'android:name': 'com.zinspector.foregroundservice.ForegroundService',
            },
        })
        mainApplication.service.push({
            $: {
                'android:name': 'com.zinspector.foregroundservice.ForegroundServiceTask',
            },
        })

        // copy the color.xml
        const srcFilePath = path.join(__dirname, 'color.xml')
        const resFilePath = path.join(
            await Paths.getResourceFolderAsync(config.modRequest.projectRoot),
            'values',
            'color.xml',
        )

        const res_dir = path.resolve(resFilePath, '..')

        if (!fs.existsSync(res_dir)) {
            await fs.promises.mkdir(res_dir)
        }

        try {
            await fs.promises.copyFile(srcFilePath, resFilePath)
        } catch (e) {
            throw e
        }

        return config
    })

    return config
}
Run Code Online (Sandbox Code Playgroud)

接下来,根据库的要求创建 color.xml

configPlugins/color.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item  name="orange"  type="color">#FF4500
    </item>
    <integer-array  name="androidcolors">
        <item>@color/orange</item>
    </integer-array>
</resources>
Run Code Online (Sandbox Code Playgroud)

现在我们需要告诉 app.config.ts 有关该插件的信息。您也可以使用 app.json,但需要withForegroundService.ts是一个 .js 文件,并且将像这样注册:plugins: [['./configPlugins/withForegroundService']]

app.config.ts

import { ExpoConfig, ConfigContext } from '@expo/config'
import { withForegroundService } from './configPlugins/withForegroundService'

export default ({ config }: ConfigContext): ExpoConfig => ({
    ...config,
    ...
    ...
    plugins: [
        withForegroundService,
    ],
})
Run Code Online (Sandbox Code Playgroud)

要测试它,只需运行npx expo prebuild,如果一切设置正确,您应该看到 中的更改android/app/src/main/AndroidManifest.xmlcolor.xml应复制到android/app/src/main/res/values/color.xml

现在唯一要做的就是配置前台任务并运行npx expo prebuild && eas build --platform=android

我测试了这种方法并让前台服务通知出现。 前台服务通知

希望这可以帮助!


小智 1

这不是一个充分的答案,但有总比没有好。我曾经创建过前台服务。面临的挑战是如何在项目中保留世博会。我正在使用 Expo Managed (EAS),但(到目前为止)无法按照此处的说明构建它。不过,如果没有世博会,这确实有效。

https://github.com/Raja0sama/rn-foreground-service

如果有更有经验的人能够解释如何运行前台服务,最好与 Expo 完全集成,但如果没有,可在通过 EAS 构建的项目中使用,请提供帮助。