diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index 0d249f73790..f27259fd635 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -9,7 +9,6 @@ import io.sentry.IScopes; import io.sentry.ISentryLifecycleToken; import io.sentry.Integration; -import io.sentry.OptionsContainer; import io.sentry.Sentry; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -98,7 +97,7 @@ public static void init( @NotNull Sentry.OptionsConfiguration configuration) { try (final @NotNull ISentryLifecycleToken ignored = staticLock.acquire()) { Sentry.init( - OptionsContainer.create(SentryAndroidOptions.class), + new SentryAndroidOptionsContainer(), options -> { final io.sentry.util.LoadClass classLoader = new io.sentry.util.LoadClass(); final boolean isTimberUpstreamAvailable = diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptionsContainer.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptionsContainer.java new file mode 100644 index 00000000000..678f7ab29b2 --- /dev/null +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptionsContainer.java @@ -0,0 +1,16 @@ +package io.sentry.android.core; + +import io.sentry.OptionsContainer; +import org.jetbrains.annotations.NotNull; + +/** + * Direct OptionsContainer for SentryAndroidOptions that avoids reflective + * getDeclaredConstructor().newInstance() on the Android startup path. + */ +final class SentryAndroidOptionsContainer extends OptionsContainer { + + @Override + public @NotNull SentryAndroidOptions createInstance() { + return new SentryAndroidOptions(); + } +} diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 4757be4894a..45268a9a894 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -2068,7 +2068,8 @@ public abstract interface class io/sentry/ObjectWriter { public abstract fun value (Z)Lio/sentry/ObjectWriter; } -public final class io/sentry/OptionsContainer { +public class io/sentry/OptionsContainer { + protected fun ()V public static fun create (Ljava/lang/Class;)Lio/sentry/OptionsContainer; public fun createInstance ()Ljava/lang/Object; } diff --git a/sentry/src/main/java/io/sentry/OptionsContainer.java b/sentry/src/main/java/io/sentry/OptionsContainer.java index 52032880aaf..b29aef2e000 100644 --- a/sentry/src/main/java/io/sentry/OptionsContainer.java +++ b/sentry/src/main/java/io/sentry/OptionsContainer.java @@ -1,28 +1,40 @@ package io.sentry; +import com.jakewharton.nopen.annotation.Open; +import io.sentry.util.Objects; import java.lang.reflect.InvocationTargetException; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal -public final class OptionsContainer { +@Open +public class OptionsContainer { public @NotNull static OptionsContainer create(final @NotNull Class clazz) { return new OptionsContainer<>(clazz); } - private final @NotNull Class clazz; + private final @Nullable Class clazz; private OptionsContainer(final @NotNull Class clazz) { super(); this.clazz = clazz; } + /** Constructor for subclasses that create the instance directly without reflection. */ + protected OptionsContainer() { + super(); + this.clazz = null; + } + public @NotNull T createInstance() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - return clazz.getDeclaredConstructor().newInstance(); + return Objects.requireNonNull(clazz, "OptionsContainer clazz is required") + .getDeclaredConstructor() + .newInstance(); } } diff --git a/sentry/src/main/java/io/sentry/util/Platform.java b/sentry/src/main/java/io/sentry/util/Platform.java index b08b6e584fb..1f7cbeb2206 100644 --- a/sentry/src/main/java/io/sentry/util/Platform.java +++ b/sentry/src/main/java/io/sentry/util/Platform.java @@ -20,16 +20,21 @@ public final class Platform { isAndroid = false; } - try { - final @Nullable String javaStringVersion = System.getProperty("java.specification.version"); - if (javaStringVersion != null) { - final @NotNull double javaVersion = Double.valueOf(javaStringVersion); - isJavaNinePlus = javaVersion >= 9.0; - } else { + if (isAndroid) { + // Android is never Java 9+, skip the system property lookup + parse on the startup path. + isJavaNinePlus = false; + } else { + try { + final @Nullable String javaStringVersion = System.getProperty("java.specification.version"); + if (javaStringVersion != null) { + final @NotNull double javaVersion = Double.valueOf(javaStringVersion); + isJavaNinePlus = javaVersion >= 9.0; + } else { + isJavaNinePlus = false; + } + } catch (Throwable e) { isJavaNinePlus = false; } - } catch (Throwable e) { - isJavaNinePlus = false; } }