模板版本:v0.4.0

react-native-background-fetch

本项目基于 react-native-background-fetch 开发。

该第三方库的仓库已迁移至 Gitcode,且支持直接从 npm 下载,新的包名为:@react-native-ohos/react-native-background-fetch。 版本所属关系如下:

三方库名称 三方库版本 发布信息 支持RN版本 Autolink 编译API版本 社区基线版本 npm地址
@react-native-ohos/react-native-background-fetch ~ 4.4.0 Gitcode Releases 0.82.* 是 API12+ 4.2.8 Npm Address
@react-native-ohos/react-native-background-fetch ~ 4.3.0 Gitcode Releases 0.77.* 否 API12+ 4.2.8 Npm Address
@react-native-ohos/react-native-background-fetch ~ 4.2.6 Gitcode Releases 0.72.* 是 API12+ 4.2.5 Npm Address
@react-native-oh-tpl/react-native-background-fetch <= 4.2.5-0.0.2@deprecated Github Releases(deprecated) 0.72.* 否 API12+ 4.2.5 Npm Address

1. 安装与使用

进入到工程目录并输入以下命令:

npm

npm install @react-native-ohos/react-native-background-fetch

yarn

yarn add @react-native-ohos/react-native-background-fetch

下面的代码展示了这个库的基本使用场景:

使用时 import 的库名不变。

import React from "react";
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  FlatList,
  StatusBar,
  Button,
} from "react-native";
import { Header, Colors } from "react-native/Libraries/NewAppScreen";
import BackgroundFetch from "react-native-background-fetch";
import {
  BackgroundFetchConfig,
  BackgroundFetchStatus,
  TaskConfig,
  NetworkType,
} from "react-native-background-fetch/src/RNBackgroundFetch";

function generateRandomString(length: number): string {
  let result = "";
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  const charactersLength = characters.length;
  for (let i = 0; i < length; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength));
  }
  return result;
}

class App extends React.Component {
  constructor(props: {}) {
    super(props);
    this.state = {
      events: [],
    };
  }

  componentDidMount() {
    // Initialize BackgroundFetch ONLY ONCE when component mounts.
    this.initBackgroundFetch();
  }

  async initBackgroundFetch(options?: BackgroundFetchConfig) {
    // BackgroundFetch event handler.
    const onEvent = async (taskId: string) => {
      console.log("[BackgroundFetch] task: ", taskId);
      // Do your background work...
      await this.addEvent(taskId);
      // IMPORTANT:  You must signal to the OS that your task is complete.
      BackgroundFetch.finish(taskId);
    };

    // Timeout callback is executed when your Task has exceeded its allowed running-time.
    // You must stop what you're doing immediately BackgroundFetch.finish(taskId)
    const onTimeout = async (taskId: string) => {
      console.warn("[BackgroundFetch] TIMEOUT task: ", taskId);
      BackgroundFetch.finish(taskId);
    };

    // Initialize BackgroundFetch only once when component mounts.
    let status = await BackgroundFetch.configure(
      { minimumFetchInterval: 20, ...options },
      onEvent,
      onTimeout
    );
  }

  scheduleTask(taskId: string, options?: TaskConfig) {
    BackgroundFetch.scheduleTask({
      taskId,
      delay: 20 * 60 * 1000, // <-- milliseconds
      ...options,
    });
  }

  // Add a BackgroundFetch event to <FlatList>
  addEvent(taskId: string) {
    // Simulate a possibly long-running asynchronous task with a Promise.
    return new Promise((resolve, reject) => {
      this.setState((state) => ({
        events: [
          ...state.events,
          {
            taskId: taskId,
            timestamp: new Date().toString(),
          },
        ],
      }));
      resolve(true);
    });
  }

  render() {
    return (
      <>
        <StatusBar barStyle="dark-content" />
        <SafeAreaView>
          <ScrollView
            contentInsetAdjustmentBehavior="automatic"
            style={styles.scrollView}
          >
            <Header />
            <View style={styles.body}>
              <View style={styles.sectionContainer}>
                <Text style={styles.sectionTitle}>BackgroundFetch Demo</Text>
              </View>
            </View>
          </ScrollView>
          <View style={styles.sectionContainer}>
            <FlatList
              data={this.state.events}
              renderItem={({ item }) => (
                <Text>
                  [{item.taskId}]: {item.timestamp}
                </Text>
              )}
              keyExtractor={(item) => generateRandomString(10)}
            />
          </View>
        </SafeAreaView>
      </>
    );
  }
}

const styles = StyleSheet.create({
  scrollView: {
    backgroundColor: Colors.lighter,
  },
  body: {
    backgroundColor: Colors.white,
  },
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: "600",
    color: Colors.black,
  },
  sectionDescription: {
    marginTop: 8,
    fontSize: 18,
    fontWeight: "400",
    color: Colors.dark,
  },
});

export default App;
是否支持autolink RN框架版本
~4.4.0 Yes 0.82
~4.3.0 No 0.77
~4.2.6 Yes 0.72
<= 4.2.5-0.0.2@deprecated No 0.72

使用AutoLink的工程需要根据该文档配置,Autolink框架指导文档:https://gitcode.com/openharmony-sig/ohos_react_native/blob/master/docs/zh-cn/Autolinking.md

如您使用的版本支持 Autolink,并且工程已接入 Autolink,可跳过ManualLink配置。

ManualLink: 此步骤为手动配置原生依赖项的指导

首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程 harmony。

2.1. Overrides RN SDK

为了让工程依赖同一个版本的 RN SDK,需要在工程根目录的 oh-package.json5 添加 overrides 字段,指向工程需要使用的 RN SDK 版本。替换的版本既可以是一个具体的版本号,也可以是一个模糊版本,还可以是本地存在的 HAR 包或源码目录。

关于该字段的作用请阅读官方说明

{
  ...
  "overrides": {
    "@rnoh/react-native-openharmony" : "./react_native_openharmony"
  }
}

2.2. 引入原生端代码

目前有两种方法:

  • 通过 har 包引入;
  • 直接链接源码。

方法一:通过 har 包引入(推荐)

har 包位于三方库安装路径的 `harmony` 文件夹下。

打开 entry/oh-package.json5,添加以下依赖

"dependencies": {
    "@rnoh/react-native-openharmony": "file:../react_native_openharmony",
    "@react-native-ohos/react-native-background-fetch": "file:../../node_modules/@react-native-ohos/react-native-background-fetch/harmony/background_fetch.har"
  }

点击右上角的 sync 按钮

或者在命令行终端执行:

cd entry
ohpm install

方法二:直接链接源码

如需使用直接链接源码,请参考[直接链接源码说明](./link-source-code.md)

2.3. 配置 CMakeLists 和引入 RNBackgroundFetchPackage

若使用的是 <= 4.2.5-0.0.2 版本,请跳过本章。

打开 entry/src/main/cpp/CMakeLists.txt,添加:

+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")

# RNOH_BEGIN: manual_package_linking_1
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-background-fetch/src/main/cpp" ./background_fetch)
# RNOH_END: manual_package_linking_1

# RNOH_BEGIN: manual_package_linking_2
+ target_link_libraries(rnoh_app PUBLIC rnoh_background_fetch)
# RNOH_END: manual_package_linking_2

打开 entry/src/main/cpp/PackageProvider.cpp,添加:

#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
+ #include "RNBackgroundFetchPackage.h"

using namespace rnoh;

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx)
{
    return {
      std::make_shared<RNOHGeneratedPackage>(ctx),
+     std::make_shared<RNBackgroundFetchPackage>(ctx)
    };
}

2.4. 在 ArkTs 侧引入 RNBackgroundFetchPackage

打开 entry/src/main/ets/RNPackagesFactory.ts,添加:

  ...
+ import { RNBackgroundFetchPackage } from "@react-native-ohos/react-native-background-fetch/ts";

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    new SamplePackage(ctx),
+   new RNBackgroundFetchPackage(ctx)
  ];
}

2.5. 必要的配置项

该模块的内容无法通过autolink自动生成,始终需要手动配置。

2.5.1. 在 ArkTs 侧引入 RNBackgroundFetchExtensionAbility(该模块始终需要手动配置)

  1. 打开 entry/src/main/ets,新建目录及 ArkTS 文件,新建一个目录并命名为 WorkSchedulerExtension。在 WorkSchedulerExtension 目录下,新建一个 ArkTS 文件并命名为 WorkSchedulerExtension.ets,用以实现延迟任务回调接口。
import { workScheduler } from "@kit.BackgroundTasksKit";
import RNBackgroundFetchExtensionAbility from "@react-native-ohos/react-native-background-fetch/src/main/ets/WorkSchedulerExtension/WorkSchedulerExtension";

export default class MyWorkSchedulerExtensionAbility extends RNBackgroundFetchExtensionAbility {
  // 延迟任务开始回调
  onWorkStart(workInfo: workScheduler.WorkInfo) {
    super.onWorkStart(workInfo);
  }

  // 延迟任务结束回调
  onWorkStop(workInfo: workScheduler.WorkInfo) {
    super.onWorkStop(workInfo);
  }
}
  1. 在entry/src/main/module.json5配置文件中注册 WorkSchedulerExtensionAbility,并设置如下标签:
{
  "module": {
    "extensionAbilities": [
      {
        "name": "MyWorkSchedulerExtensionAbility",
        "srcEntry": "./ets/WorkSchedulerExtension/WorkSchedulerExtension.ets",
        "type": "workScheduler"
      }
    ]
  }
}

2.6 运行

点击右上角的 sync 按钮

或者在命令行终端执行:

cd entry
ohpm install

然后编译、运行即可。

3. 约束与限制

3.1. 兼容性

本文档内容基于以下版本验证通过:

  1. RNOH: 0.72.96; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;
  2. RNOH: 0.72.33; SDK: HarmonyOS NEXT B1; IDE: DevEco Studio: 5.0.3.900; ROM: Next.0.0.71;
  3. RNOH: 0.77.18; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;
  4. RNOH: 0.82.1; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;

4. API

"Platform"列表示该属性在原三方库上支持的平台。

"HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。

BackgroundFetch

Name Description Type Required Platform HarmonyOS Support
configure 初始化BackgroundFetch配置,包括配置选项和回调。[[start]]方法将自动执行。 Function yes all partially
scheduleTask 在最初提供给 configure 的监听器之外,添加额外的后台事件监听器。 Function no all partially
start 开始订阅后台事件。 Function no all yes
stop 停止订阅后台事件。 Function no all yes
finish 必须在回调中执行 finish 来表示任务完成。 Function no all yes
status 查询后台任务状态。 Function no all yes

此表格列出了所有接口。有关详细参数说明,请参阅下面的章节。

BackgroundFetch.configure()

BackgroundFetch.configure(config: BackgroundFetchConfig, onEvent: (taskId:string) => void, onTimeout?:(taskId:string) => void): Promise<BackgroundFetchStatus>;
Name Description Type Required Platform HarmonyOS Support
BackgroundFetchConfig.minimumFetchInterval 执行后台任务的最小时间间隔(分钟)。默认为 15 分钟。最小值为 15 分钟。 number no all yes
BackgroundFetchConfig.stopOnTerminate 设置为 false 以在用户终止应用后继续后台任务。默认为 true。 boolean no Android no
BackgroundFetchConfig.startOnBoot 设置为 true 以在设备重启时启动后台任务。默认为 false。 boolean no Android no
BackgroundFetchConfig.enableHeadless 设置为 true 以启用 Headless 机制,用于在应用终止后处理 后台任务。 boolean no Android no
BackgroundFetchConfig.forceAlarmManager 默认情况下,插件会在可能时使用 Android 的 JobScheduler。JobScheduler API 优先考虑电池寿命,根据设备使用情况和电池水平调节任务执行。 boolean no Android no
BackgroundFetchConfig.requiredNetworkType 设置所需网络类型的详细描述。 NetworkType no Android partially (NETWORK_TYPE_ANY, NETWORK_TYPE_CELLULAR, NETWORK_TYPE_UNMETERED)
BackgroundFetchConfig.requiresBatteryNotLow 指定要运行此作业,设备电池电量不得过低。 boolean no Android yes
BackgroundFetchConfig.requiresStorageNotLow 指定要运行此作业,设备可用存储空间不得过低。 boolean no Android yes
BackgroundFetchConfig.requiresCharging 指定要运行此任务,设备必须正在充电(或为连接到永久电源的非电池供电设备,如 Android TV 设备)。 boolean no Android yes
BackgroundFetchConfig.requiresDeviceIdle 设置为 true 时,确保设备在活跃使用时不会运行此作业。 boolean no Android yes

BackgroundFetch.scheduleTask()

BackgroundFetch.scheduleTask(config: TaskConfig): Promise<boolean>;
Name Description Type Required Platform HarmonyOS Support
TaskConfig.taskId 任务名称 string yes all yes
TaskConfig.delay 执行此任务的最小时间间隔(毫秒)。 number yes all yes
TaskConfig.periodic 设置此任务是周期执行还是仅执行一次。 boolean no all yes
TaskConfig.requiresNetworkConnectivity 设置为 true 时,确保设备有网络连接时才运行此作业。 boolean no all no
TaskConfig.stopOnTerminate 设置为 false 以在用户终止应用后继续后台任务。默认为 true。 boolean no Android no
TaskConfig.startOnBoot 设置为 true 以在设备重启时启动后台任务。默认为 false。 boolean no Android no
TaskConfig.enableHeadless 设置为 true 以启用 Headless 机制,用于在应用终止后处理后台任务。 boolean no Android no
TaskConfig.forceAlarmManager 默认情况下,插件会在可能时使用 Android 的 JobScheduler。JobScheduler API 优先考虑电池寿命,根据设备使用情况和电池水平调节任务执行。 boolean no Android no
TaskConfig.requiredNetworkType 设置所需网络类型的详细描述。 NetworkType no Android partially (NETWORK_TYPE_ANY, NETWORK_TYPE_CELLULAR, NETWORK_TYPE_UNMETERED)
TaskConfig.requiresBatteryNotLow 指定要运行此任务,设备电池电量不得过低。 boolean no Android yes
TaskConfig.requiresStorageNotLow 指定要运行此任务,设备可用存储空间不得过低。 boolean no Android yes
TaskConfig.requiresCharging 指定要运行此任务,设备必须正在充电(或为连接到永久电源的非电池供电设备,如 Android TV 设备)。 boolean no Android yes
TaskConfig.requiresDeviceIdle 设置为 true 时,确保设备在活跃使用时不会运行此任务。 boolean no Android yes

BackgroundFetch.stop()

BackgroundFetch.stop(taskId?: string): Promise<boolean>;
Name Description Type Required Platform HarmonyOS Support
taskId 任务名称 string no all yes

BackgroundFetch.finish()

BackgroundFetch.finish(taskId: string): void;
Name Description Type Required Platform HarmonyOS Support
taskId 任务名称 string yes all yes

BackgroundFetch.status()

BackgroundFetch.status(callback?: (status: BackgroundFetchStatus) => void): Promise<BackgroundFetchStatus>;
Name Description Type Required Platform HarmonyOS Support
callback 后台任务状态回调。 Function no all yes

BackgroundFetchStatus

Name Description Type Required Platform HarmonyOS Support
BackgroundFetchStatus.STATUS_RESTRICTED 后台任务更新不可用,用户无法再次启用。例如,当当前用户启用了家长控制时,可能会出现此状态。 BackgroundFetchStatus yes iOS no
BackgroundFetchStatus.STATUS_DENIED 用户明确禁用了此应用或整个系统的后台行为。 BackgroundFetchStatus yes iOS no
BackgroundFetchStatus.STATUS_AVAILABLE 后台任务可用且已启用。 BackgroundFetchStatus yes all yes

5. 遗留问题

6. 其他

  • iOS可能需要数天的时间才能启动机器学习算法并开始触发常规事件。同时,您应该定期将应用程序带到前台,以使用用户的行为训练iOS机器学习算法。之后iOS才会定期执行任务。
  • Android中,不需要机器算法介入,configure函数可以每15分钟调用一次,scheduleTask可以最低每隔1分钟调用一次。
  • HarmonyOS中,系统会根据内存、功耗、设备温度、用户使用习惯等统一调度,如当系统内存资源不足或温度达到一定挡位时,系统将延迟调度该任务。假如你设置20分钟后执行,第一次任务不一定是20分钟就会执行,有可能十几分钟就执行了,往后的任务得起码2小时以后才会执行,更多详情请查看下面的链接内容。https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/work-scheduler-V5

7. 开源协议

本项目基于 The MIT License (MIT) ,请自由地享受和参与开源。