You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

应用关闭时如何从Android Worker运行React Native Headless JS任务?

问题

我的React Native Android应用发布在多个应用商店,需要给从特定商店下载的应用实例推送专属通知。但用户可能通过其他商店更新应用,所以得把这个情况同步给服务器。我通过监听广播来处理这个场景,代码如下:

广播接收器代码

package com.wbdigitalmobile;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;

public class PackageReplacedReceiver extends BroadcastReceiver {
  private static final String TAG = "PackageReplacedReceiver";

  @Override
  public void onReceive(Context context, Intent intent) {
    Log.d(TAG, "onReceive");

    OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(PackageReplacedWorker.class).build();

    WorkManager.getInstance(context).enqueue(request);
  }
}

Worker代码

package com.wbdigitalmobile;

import android.content.Context;
import android.content.Intent;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

import com.facebook.react.HeadlessJsTaskService;

public class PackageReplacedWorker extends Worker {
  private static final String TAG = "PackageReplacedWorker";
  private final Context context;

  public PackageReplacedWorker (Context context, WorkerParameters workerParams ) {
    super ( context, workerParams );
    this.context = context;
  }

  @NonNull
  @Override
  public Result doWork () {
    Log.d(TAG, "doWork");

    Intent serviceIntent = new Intent(this.context, PushNotificationTokenUpdateService.class);

    this.context.startService(serviceIntent);
    HeadlessJsTaskService.acquireWakeLockNow(this.context);

    return Result.success ();
  }
}

Headless服务代码

package com.wbdigitalmobile;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Intent;
import android.util.Log;

import androidx.core.app.NotificationCompat;

import com.facebook.react.HeadlessJsTaskService;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.jstasks.HeadlessJsTaskConfig;

import javax.annotation.Nullable;

public class PushNotificationTokenUpdateService extends HeadlessJsTaskService {
  private static final String TAG = "PushNotificationTokenUpdateService";
  public static final int SERVICE_ID = 800000;
  private static final String CHANNEL_ID = "PushNotificationTokenUpdateService";

  private Notification buildNotification() {
    final String taskTitle = "taskTitle";
    final String taskDesc = "taskDesc";

    final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
      .setContentTitle(taskTitle)
      .setContentText(taskDesc);

    return builder.build();
  }

  private void createNotificationChannel() {
    final int importance = NotificationManager.IMPORTANCE_LOW;

    final NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "taskTitle", importance);

    channel.setDescription("taskDesc");

    final NotificationManager notificationManager = getSystemService(NotificationManager.class);
    
    notificationManager.createNotificationChannel(channel);
  }

  @Override
  public void onCreate() {
    super.onCreate();

    createNotificationChannel();

    startForeground(SERVICE_ID, buildNotification());
  }

  @Override
  protected @Nullable HeadlessJsTaskConfig getTaskConfig(Intent intent) {
    Log.d(TAG, "getTaskConfig");

    return new HeadlessJsTaskConfig(
      "PushNotificationTokenUpdateService",
      Arguments.createMap(),
      5000,
      true
    );
  }
}

现在遇到的问题是:尝试运行React Native Headless JS任务时,应用关闭状态下无法从Worker中启动前台任务。请问如何在应用关闭时从Worker运行React Native Headless JS任务?


解决方案

1. 适配Android O+的前台服务限制

从Android 8.0(API 26)开始,后台直接启动服务会被系统禁止,必须使用startForegroundService()替代startService(),且需在服务启动后的10秒内调用startForeground()显示前台通知。

修改PackageReplacedWorker中的启动服务代码:

import android.os.Build;

// ...

@NonNull
@Override
public Result doWork () {
  Log.d(TAG, "doWork");

  Intent serviceIntent = new Intent(this.context, PushNotificationTokenUpdateService.class);
  
  // 适配Android O+的前台服务规则
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    context.startForegroundService(serviceIntent);
  } else {
    context.startService(serviceIntent);
  }
  
  HeadlessJsTaskService.acquireWakeLockNow(this.context);

  return Result.success();
}

2. 完善Manifest权限与组件声明

在AndroidManifest.xml中添加必要权限并注册组件:

<!-- 前台服务权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 保持设备唤醒权限 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />

<!-- 注册应用替换广播接收器 -->
<receiver android:name=".PackageReplacedReceiver"
          android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
  </intent-filter>
</receiver>

<!-- 注册Headless服务 -->
<service android:name=".PushNotificationTokenUpdateService"
         android:enabled="true"
         android:exported="false" />

3. 优化前台通知的合规性

确保通知符合系统要求,避免被拦截:

private Notification buildNotification() {
  final String taskTitle = "应用更新同步";
  final String taskDesc = "正在同步应用商店信息到服务器";

  final NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
    .setContentTitle(taskTitle)
    .setContentText(taskDesc)
    .setSmallIcon(R.mipmap.ic_launcher) // 必须设置小图标,否则通知无法正常显示
    .setPriority(NotificationCompat.PRIORITY_LOW);

  return builder.build();
}

private void createNotificationChannel() {
  final int importance = NotificationManager.IMPORTANCE_LOW;

  final NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "应用更新同步通道", importance);
  channel.setDescription("用于同步应用商店更新信息的前台通知通道");

  final NotificationManager notificationManager = getSystemService(NotificationManager.class);
  if (notificationManager != null) {
    notificationManager.createNotificationChannel(channel);
  }
}

4. 正确注册Headless JS任务

在React Native的JS入口文件(如index.js)中注册任务:

import { AppRegistry } from 'react-native';
import App from './App';
import { name as appName } from './app.json';

// 注册Headless任务
AppRegistry.registerHeadlessTask(
  'PushNotificationTokenUpdateService',
  () => require('./services/PushNotificationTokenUpdateTask')
);

AppRegistry.registerComponent(appName, () => App);

对应的任务逻辑文件PushNotificationTokenUpdateTask.js示例:

module.exports = async (taskData) => {
  // 编写同步服务器的业务逻辑,比如获取应用安装来源并上报
  try {
    // 示例:获取应用安装来源(需自行实现相关原生模块)
    const installSource = await getAppInstallSource();
    // 发送请求到服务器
    await fetch('https://your-server-domain/update-source', {
      method: 'POST',
      body: JSON.stringify({ source: installSource }),
      headers: {
        'Content-Type': 'application/json',
      },
    });
  } catch (error) {
    console.error('同步应用来源失败:', error);
  }
  return Promise.resolve();
};

5. 提升Worker的执行稳定性

给Worker添加约束和重试策略,避免任务被系统杀死:

import androidx.work.Constraints;
import androidx.work.NetworkType;
import androidx.work.BackoffPolicy;
import java.util.concurrent.TimeUnit;

// ...

// 在PackageReplacedReceiver中修改Worker请求构建逻辑
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(PackageReplacedWorker.class)
  .setConstraints(new Constraints.Builder()
    .setRequiredNetworkType(NetworkType.CONNECTED) // 仅在有网络时执行
    .build())
  .setBackoffCriteria(BackoffPolicy.LINEAR, OneTimeWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS)
  .build();

WorkManager.getInstance(context).enqueue(request);

内容的提问来源于stack exchange,提问作者Alexander Pankin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 22:08:10