Как обрабатывать фоновые задачи в реагировании натив с помощью безголового JS

Что такое безголовый JS в React Native?

Headless JS - это встроенная функция React Native, которая позволяет выполнять код JavaScript в фоновом режиме, даже когда пользовательский интерфейс приложения не активен. В отличие от выполнения на переднем плане, где приложение видимо и интерактивно, Headless JS запускает задачи как отдельные процессы, которые не зависят от основного потока пользовательского интерфейса. Это важно для операций, которые должны продолжаться после того, как пользователь закроет приложение или до того, как он откроет его - например, синхронизация данных, обработка входящих push-уведомлений или обработка критически важных по времени оповещений. Headless JS сокращает разрыв между временем выполнения React Native и нативными уровнями Android и iOS, позволяя писать кроссплатформенную фоновую логику с минимальным нативным кодом.

Случаи общего использования для безголовых JS

Понимание того, когда использовать Headless JS, помогает вам решить, подходит ли он вашему проекту.

Безголовый JS не предназначен для длительных или тяжелых вычислений - система может убить ваш фоновый процесс, если это займет слишком много времени. Для расширенной фоновой работы объедините его с нативными службами, такими как WorkManager (Android) или BackgroundTasks (iOS).

Настройка безголового JS в React Native: Step-by-Step

Внедрение Headless JS включает две основные части: создание обработчика JavaScript и регистрация его в родном коде для каждой платформы.

1.Создать JavaScript Background Handler

Начните с создания нового файла, например . Этот файл будет содержать функцию, которая запускается при запуске фоновой задачи. Функция получает объект , который может удерживать параметры, переданные с нативной стороны.

// BackgroundTask.js
import { AppRegistry } from 'react-native';

const backgroundTask = async (taskData) => {
 console.log('Background task started with data:', taskData);
 // Your background logic goes here
 // For example, fetch new data from an API
 try {
 const response = await fetch('https://api.example.com/sync');
 const data = await response.json();
 // Store data in AsyncStorage or a local database
 console.log('Background sync complete');
 } catch (error) {
 console.error('Background task failed:', error);
 // Optionally throw an error to let the system know
 }
};

AppRegistry.registerHeadlessTask('BackgroundTask', () => backgroundTask);

Обратите внимание, что обработчик должен быть функцией, которая возвращает Обещание (или асинхронную функцию). Система будет ждать, пока Обещание будет решено, прежде чем рассматривать задачу как завершенную. Если Обещание отклоняется, нативная сторона может рассматривать его как сбой.

2.Зарегистрируйте задачу в исходном коде Android

Android требует, чтобы вы зарегистрировали задачу без головы в своем (или в зависимости от версии React Native). Кроме того, вам нужен HeadlessJsTaskService для управления жизненным циклом задачи.

Шаг А — Обновление MainApplication.java

// android/app/src/main/java/com/yourapp/MainApplication.java
import com.facebook.react.HeadlessJsTaskService;

public class MainApplication extends Application implements ReactApplication {
 // ... existing code

 @Override
 public void onCreate() {
 super.onCreate();
 // Initialize ReactNative instance as usual
 // The task will be registered via AppRegistry in JavaScript
 }
}

Для самой задачи не требуется явной регистрации в Java, если вы используете — React Native автоматически отображает обработчик JavaScript на родную задачу.

Шаг B — Создайте службу без головы

Создайте новый класс Java в том же пакете:

// android/app/src/main/java/com/yourapp/BackgroundTaskService.java
package com.yourapp;

import android.content.Intent;
import android.os.Bundle;
import com.facebook.react.HeadlessJsTaskService;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.jstasks.HeadlessJsTaskConfig;

public class BackgroundTaskService extends HeadlessJsTaskService {
 @Override
 protected HeadlessJsTaskConfig getTaskConfig(Intent intent) {
 Bundle extras = intent.getExtras();
 if (extras != null) {
 return new HeadlessJsTaskConfig(
 "BackgroundTask",
 Arguments.fromBundle(extras),
 5000, // timeout in ms
 true // allows task to run in foreground as well
 );
 }
 return null;
 }
}

Этот сервис извлекает любые дополнительные данные из намерения (например, данные из уведомления) и передает их обработчику JavaScript.

Шаг C — Добавьте сервис на AndroidManifest.xml

<service android:name=".BackgroundTaskService" />

Теперь любой нативный модуль может запустить эту услугу с помощью Intent, или вы можете использовать широковещательный приемник, чтобы запустить его из push-уведомления.

3.Настройка возможностей резервного копирования iOS

Для использования Headless JS на iOS вы должны включить соответствующие фоновые режимы и использовать нативное планирование (например, push-уведомления или фоновый выбор).

Шаг А — Включить фоновые режимы

Откройте свой проект Xcode, перейдите в Signing & Capabilities и добавьте Background Modes возможность. Проверьте режимы, необходимые вашему приложению, например, «Background fetch» или «Удалённые уведомления».

Шаг B — Внедрение методов делегирования приложений

В , реализовать необходимые методы делегата, чтобы получить фоновые события и начать безголовую задачу.

// AppDelegate.m (Objective-C)
#import <React/RCTAppDelegate.h>
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTRootView.h>

@implementation AppDelegate

- (void)application:(UIApplication *)application
 performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
 // You can get the bridge from your ReactNative root view
 RCTBridge *bridge = [self.rootView bridge];
 [bridge.eventDispatcher sendAppEventWithName:@"BackgroundFetch"
 body:@{@"taskName": @"BackgroundTask"}];
 completionHandler(UIBackgroundFetchResultNewData);
}

@end

Со стороны JavaScript нужно прослушать событие , а затем вручную запустить задачу без головы.Однако, Headless JS на iOS чаще используется вместе с push-уведомлениями — когда приходит уведомление, вы можете выполнить код без головы перед показом оповещения.

Для push-уведомлений, введите в AppDelegate и отправьте событие в React Native.

Пробные задачи Background Tasks

После настройки вы можете запустить задачу безголового JS из различных источников:

Пример: Запуск службы Android с приемника вещания, который слушает пользовательские действия:

public class AlarmReceiver extends BroadcastReceiver {
 @Override
 public void onReceive(Context context, Intent intent) {
 Intent serviceIntent = new Intent(context, BackgroundTaskService.class);
 serviceIntent.putExtra("source", "alarm");
 context.startService(serviceIntent);
 }
}

Затем зарегистрируйте приемник в AndroidManifest.xml и запланируйте сигнализацию с помощью .

Лучшие практики для надежной фоновой операции

Фоновые задачи по своей природе хрупки - ОС может убить ваш процесс в любое время. Следуйте этим рекомендациям, чтобы сделать ваши задачи без головы надежными:

Отладка безголовых JS-задач

Отладка фонового кода сложна, потому что вы не можете использовать Chrome DevTools инспектор, когда пользовательский интерфейс не активен.

Ограничения и соображения

Безголовый JS не серебряная пуля. Поймите его ограничения, чтобы избежать сюрпризов:

Сравнение с другими фоновыми библиотеками

Безголовый JS от React Native является встроенным механизмом самого низкого уровня. Для многих случаев использования пакеты сообщества обеспечивают более удобный API:

LibraryPlatformFeatures
react-native-background-fetchAndroid & iOSPeriodic background fetching with system‑managed scheduling; supports Headless JS internally.
react-native-background-actionsAndroid & iOSRuns a long‑running foreground service (with persistent notification) – can execute heavy tasks.
WorkManager (Android only via native bridge)AndroidDeferrable, reliable background work with constraints (network, battery); does not use Headless JS by default.
Headless JS (vanilla)Android & iOSSimple, built‑in, no extra dependencies; best for short tasks triggered by push or alarms.

Если ваша задача должна выполняться даже после перезагрузки или вам нужно сложное планирование, рассмотрите возможность обертывания реализации нативного WorkManager и разоблачения его с помощью модуля React Native. Headless JS остается отличным для легких «огненных» заданий.

Полный пример: Синхронизация данных с Push-уведомлением

Давайте все объединим с реалистичным сценарием: когда на Android поступает тихое push-уведомление, приложение загружает последние данные из API и хранит их локально. Пользователь никогда не видит изменения пользовательского интерфейса - данные готовы, когда они в следующий раз откроют приложение.

Android — Push-получатель

Создайте приемник вещания, который слушает действие толчка (например, из FCM):

public class PushReceiver extends BroadcastReceiver {
 @Override
 public void onReceive(Context context, Intent intent) {
 Intent serviceIntent = new Intent(context, BackgroundTaskService.class);
 serviceIntent.putExtra("action", "syncData");
 context.startService(serviceIntent);
 }
}

Зарегистрируйте его в AndroidManifest.xml с помощью фильтра намерений для вашего push-действия (или используйте FirebaseMessagingService , чтобы запустить сервис напрямую).

JavaScript Handler

// BackgroundTask.js (expanded)
import { AppRegistry } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';

const backgroundTask = async (taskData) => {
 const { action } = taskData;
 if (action === 'syncData') {
 try {
 const response = await fetch('https://api.example.com/latest-data');
 const json = await response.json();
 await AsyncStorage.setItem('cachedData', JSON.stringify(json));
 console.log('Data synced successfully');
 } catch (error) {
 // Write error to a local log
 const errorLog = await AsyncStorage.getItem('errorLog');
 const newLog = (errorLog ? errorLog + '\n' : '') + new Date().toISOString() + ': ' + error.message;
 await AsyncStorage.setItem('errorLog', newLog);
 }
 }
};

AppRegistry.registerHeadlessTask('BackgroundTask', () => backgroundTask);

Когда пользователь вновь открывает приложение, передний план может читать и немедленно обновлять пользовательский интерфейс, создавая бесшовный офлайн-первый опыт.

Производительность и влияние батареи

Головные задачи выполняются в том же движке JavaScript, что и основное приложение, но в отдельном контексте. Каждая задача раскручивает новый экземпляр JS без головы, который потребляет память и процессор. Чтобы минимизировать разрядку батареи:

Заключение

Headless JS является мощным инструментом в поясе разработчика React Native для обработки коротких фоновых задач, которые должны выполняться независимо от видимости приложения. Настраивая обработчик JavaScript и соединяя его с нативными фоновыми триггерами - такими как push-уведомления, тревоги или фоновый вывод - вы можете создавать адаптивные приложения, управляемые данными, которые всегда ощущаются. Не забудьте следовать лучшим практикам в отношении краткости, обработки ошибок и ограничений для платформы, чтобы гарантировать, что ваши фоновые операции являются надежными и удобными для батареи. Для расширенного планирования или более длительных задач дополните Headless JS выделенными библиотеками, такими как или нативный WorkManager.

Для дальнейшего чтения обратитесь к официальной документации React Native по Headless JS (Android) и Headless JS (iOS) . Также проверьте библиотеку react-native-background-fetch для периодического управления задачами.