应用关闭时如何从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
相关产品推荐
相关产品推荐

