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

React Native应用后台时Unity Ads的AD_COMPLETED事件触发问题

问题描述

在React Native中集成Unity Ads原生模块时遇到了一个问题:当广告播放导致应用进入后台后,AD_COMPLETED事件只有等应用回到前台时才会触发。我需要实现广告在后台完成时立即触发该事件,这样才能及时执行后续任务(比如启动挖矿逻辑),就像react-native-google-mobile-ads插件里Admob广告的正常表现那样。

原因分析

默认用DeviceEventManagerModule.RCTDeviceEventEmitter发送事件时,依赖React Native的JS线程调度。当应用处于后台时,JS线程可能被系统挂起,导致事件没法即时传递到JS层。而成熟的广告插件会用更可靠的事件传递机制,确保后台事件能即时触发。

解决方案

核心思路

通过Android的HeadlessJsTaskService创建后台任务,确保广告完成的事件在后台也能触发JS逻辑;同时区分前台/后台场景,分别用直接发送事件和启动Headless任务的方式处理。

具体实现步骤

1. Android端代码修改

第一步:创建Headless任务服务

新建UnityAdsHeadlessService.java文件:

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

import android.content.Intent;
import android.os.Bundle;

public class UnityAdsHeadlessService extends HeadlessJsTaskService {
    @Override
    protected HeadlessJsTaskConfig getTaskConfig(Intent intent) {
        Bundle extras = intent.getExtras();
        WritableMap data = Arguments.createMap();
        if (extras != null) {
            data.putString("adStatus", extras.getString("adStatus"));
        }
        return new HeadlessJsTaskConfig(
            "UnityAdsBackgroundTask",
            data,
            5000, // 任务超时时间,单位毫秒
            false // 仅允许后台执行
        );
    }
}
第二步:在AndroidManifest.xml注册服务

在<application>标签内添加:

<service android:name=".UnityAdsHeadlessService" />
第三步:修改事件发送逻辑

替换原有的sendEvent方法,新增判断应用前后台的逻辑:

import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import java.util.List;

private void sendEventOrStartHeadlessTask(String eventName, WritableMap params) {
    // 判断应用是否处于后台
    if (isAppInBackground(reactContext)) {
        // 启动Headless任务处理后台事件
        Intent intent = new Intent(reactContext, UnityAdsHeadlessService.class);
        intent.putExtra("adStatus", params.getString("adStatus"));
        reactContext.startService(intent);
    } else {
        // 前台时直接发送事件到JS
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName, params);
    }
}

// 判断应用是否在后台的工具方法
private boolean isAppInBackground(Context context) {
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
    if (appProcesses == null) return false;
    
    final String packageName = context.getPackageName();
    for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
        if (appProcess.processName.equals(packageName) && appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
            return true;
        }
    }
    return false;
}
第四步:更新Unity Ads监听器

把原来的sendEvent调用替换为新的方法,同时注意每次创建新的WritableMap避免数据覆盖:

@Override
public void onUnityAdsShowComplete(String placementId, UnityAds.UnityAdsShowCompletionState state) {
    Log.v("UnityAdsExample", "onUnityAdsShowComplete: " + placementId);
    WritableMap params = Arguments.createMap();
    if (state.equals(UnityAds.UnityAdsShowCompletionState.COMPLETED)) {
        params.putString("adStatus", "fullComplete");
        sendEventOrStartHeadlessTask("AD_COMPLETED", params);
    } else {
        params.putString("adStatus", "partialComplete");
        sendEventOrStartHeadlessTask("AD_COMPLETED", params);
    }
}

@Override
public void onUnityAdsShowFailure(String placementId, UnityAds.UnityAdsShowError error, String message) {
    Log.e("UnityAdsExample", "Unity Ads failed to show ad for " + placementId + " with error: [" + error + "] " + message);
    WritableMap params = Arguments.createMap();
    params.putString("adStatus", "adShowFail");
    sendEventOrStartHeadlessTask("AD_COMPLETED", params);
}

2. JS端代码修改

第一步:注册Headless任务

在项目入口文件(比如index.js)中添加:

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

// 注册Unity Ads后台任务
AppRegistry.registerHeadlessTask('UnityAdsBackgroundTask', () =>
  require('./UnityAdsBackgroundTask')
);

AppRegistry.registerComponent(appName, () => App);
第二步:创建后台任务处理文件

新建UnityAdsBackgroundTask.js:

import AsyncStorage from '@react-native-async-storage/async-storage';

module.exports = async (taskData) => {
  const { adStatus } = taskData;
  console.log("Unity Ad后台事件触发:", adStatus);
  
  // 后台执行后续任务,比如标记需要启动挖矿
  if (adStatus === "fullComplete") {
    console.log("后台标记启动挖矿...");
    await AsyncStorage.setItem('shouldStartMining', 'true');
  }
  
  return Promise.resolve();
};
第三步:更新组件内的事件监听

修改原有的useEffect,增加后台任务状态检查:

import AsyncStorage from '@react-native-async-storage/async-storage';

React.useEffect(() => {
  // 组件挂载时检查后台是否已经触发了挖矿标记
  const checkBackgroundFlag = async () => {
    const shouldStart = await AsyncStorage.getItem('shouldStartMining');
    if (shouldStart === 'true') {
      setMiningAfterAd(true);
      await AsyncStorage.removeItem('shouldStartMining');
    }
  };
  checkBackgroundFlag();

  const loadEventEmitter = new NativeEventEmitter();
  const showEventEmitter = new NativeEventEmitter();

  const loadEventListener = loadEventEmitter.addListener(
    'AD_LOADED', 
    (event) => {
      console.log("Unity Ad加载完成");
      console.log(event.adStatus);

      if(showAdTimeOutRef.current){
        setShowAdTimeOut(false);
        return;
      } else {
        setAdIsLoaded(true);
        UnityAdsModule.showAd();
      }
  });

  const showEventListener = showEventEmitter.addListener(
    'AD_COMPLETED', 
    (event) => {
      console.log("Unity Ad播放完成");
      console.log(event.adStatus);

      if(event.adStatus === "fullComplete"){
        console.log("启动挖矿...");
        setMiningAfterAd(true);
      }
  });

  return () => {
    loadEventListener.remove();
    showEventListener.remove();
  };

}, []);

关键注意事项

  • Headless任务执行时间不能过长,否则会被系统回收;如果需要长时间运行,要配合前台服务使用。
  • 后台任务无法直接更新React组件状态,需要用本地存储(如AsyncStorage)或全局状态管理工具传递状态,前台组件挂载时再读取更新。
  • Android 8.0及以上启动后台服务需要FOREGROUND_SERVICE权限,若任务执行时间较长,需将服务设为前台服务。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 06:32:15