upgrade-support icon indicating copy to clipboard operation
upgrade-support copied to clipboard

HeadlessJsTaskService getReactNativeHost override

Open naquilini opened this issue 5 months ago • 0 comments

Environment

System:
  OS: macOS 13.6
  CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
  Memory: 137.46 MB / 16.00 GB
  Shell:
    version: 3.2.57
    path: /bin/bash
Binaries:
  Node:
    version: 18.20.8
    path: ~/.nvm/versions/node/v18.20.8/bin/node
  Yarn:
    version: 1.22.19
    path: /usr/local/bin/yarn
  npm:
    version: 10.8.2
    path: ~/.nvm/versions/node/v18.20.8/bin/npm
  Watchman:
    version: 2024.12.02.00
    path: /usr/local/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /usr/local/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.2
      - iOS 17.2
      - macOS 14.2
      - tvOS 17.2
      - visionOS 1.0
      - watchOS 10.2
  Android SDK: Not Found
IDEs:
  Android Studio: 2024.3 AI-243.26053.27.2432.13536105
  Xcode:
    version: 15.2/15C500b
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.15
    path: /Users/ipaddev/.jenv/shims/javac
  Ruby:
    version: 2.6.10
    path: /usr/bin/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 18.0.0
    wanted: 18.0.0
  react:
    installed: 19.0.0
    wanted: 19.0.0
  react-native:
    installed: 0.79.5
    wanted: 0.79.5
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: false
  newArchEnabled: false
iOS:
  hermesEnabled: false
  newArchEnabled: false

Things I’ve done to figure out my issue

Upgrading version

0.79.5

Description

I'm upgrading from RN 0.78.3 to RN 0.79.5. I have a custom module to handle push notifications. On Android I implemented a background service which extends HeadlessJsTaskService and overrides getReactNativeHost, as my application does not implement ReactApplication. When upgrading to RN 0.79.5 I get a build error as getReactNativeHost since this RN version has been marked as final:

error: getReactNativeHost() in PushHeadless cannot override getReactNativeHost() in HeadlessJsTaskService protected ReactNativeHost getReactNativeHost() { ^ overridden method is final

I checked HeadlessJsTaskService.kt source file (https://github.com/facebook/react-native/blob/v0.79.5/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/HeadlessJsTaskService.kt) and it still reports this in the comments:

/**
   * Get the [ReactNativeHost] used by this app. By default, assumes [getApplication] is an instance
   * of [ReactApplication] and calls [ReactApplication.reactNativeHost].
   *
   * Override this method if your application class does not implement `ReactApplication` or you
   * simply have a different mechanism for storing a `ReactNativeHost`, e.g. as a static field
   * somewhere.
   */
  protected val reactNativeHost: ReactNativeHost
    get() = (application as ReactApplication).reactNativeHost

But in ReactAndroid.api (https://github.com/facebook/react-native/blob/v0.79.5/packages/react-native/ReactAndroid/api/ReactAndroid.api), the method is declared as follows:

public abstract class com/facebook/react/HeadlessJsTaskService : android/app/Service, com/facebook/react/jstasks/HeadlessJsTaskEventListener {
	...
	protected final fun getReactNativeHost ()Lcom/facebook/react/ReactNativeHost;
	...
}

Is this comment still accurate? Is there still a way to override getReactNativeHost in custom headless tasks?

Reproducible demo

Create custom Task service which extend HeadlessJsTaskService and try to override method getReactNativeHost

Example:

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

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

import java.lang.reflect.InvocationTargetException;
import javax.annotation.Nullable;

/**
 * called from PushMessagingService if app is closed or in background
 */
public class PushHeadless extends HeadlessJsTaskService {
	private static final long TIMEOUT_DEFAULT = 60000;

	@Override
	protected ReactNativeHost getReactNativeHost() {
		return PushReactHost.getReactNativeHost(getApplication());
	}

	@Override
	protected @Nullable
	HeadlessJsTaskConfig getTaskConfig(Intent intent) {
		try {
			if (intent == null) {
				return null;
			}

			Bundle extras = intent.getExtras();
			if (extras == null) {
				return null;
			}

			WritableMap messageMap = ((Push) intent.getParcelableExtra(PushHandler.MESSAGE)).toMap();
			if(messageMap != null) {
				// Prevents race condition where the user opens the app at the same time as a notification
				// is delivered, causing a crash.
				return new HeadlessJsTaskConfig(getTaskName(), messageMap, TIMEOUT_DEFAULT, true);
			}
		} catch (Exception e){
			e.printStackTrace();
		}
		return null;
	}

	protected String getTaskName(){
		return "TaHeadlessTask";
	}

naquilini avatar Jul 14 '25 09:07 naquilini