diff --git a/CHANGELOG.md b/CHANGELOG.md
index 20e6b36fd..fcdcc21ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
## Unreleased
+### Fixes
+
+- The SDK no longer crashes on Android versions 5 and 6 with native support enabled ([#1652](https://github.com/getsentry/sentry-unity/pull/1652))
+
### Dependencies
- Bump Cocoa SDK from v8.25.2 to v8.26.0 ([#1648](https://github.com/getsentry/sentry-unity/pull/1648))
diff --git a/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs b/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs
index 92eaf8ee6..013c80833 100644
--- a/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs
+++ b/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs
@@ -8,69 +8,85 @@ namespace Sentry.Unity.Android
///
public class AndroidJavaScopeObserver : ScopeObserver
{
- public AndroidJavaScopeObserver(SentryOptions options) : base("Android", options) { }
+ private readonly JniExecutor _jniExecutor;
+
+ public AndroidJavaScopeObserver(SentryOptions options, JniExecutor jniExecutor) : base("Android", options)
+ {
+ _jniExecutor = jniExecutor;
+ }
private AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry");
public override void AddBreadcrumbImpl(Breadcrumb breadcrumb)
{
- AndroidJNI.AttachCurrentThread();
- using var sentry = GetSentryJava();
- using var javaBreadcrumb = new AndroidJavaObject("io.sentry.Breadcrumb");
- javaBreadcrumb.Set("message", breadcrumb.Message);
- javaBreadcrumb.Set("type", breadcrumb.Type);
- javaBreadcrumb.Set("category", breadcrumb.Category);
- using var javaLevel = breadcrumb.Level.ToJavaSentryLevel();
- javaBreadcrumb.Set("level", javaLevel);
- sentry.CallStatic("addBreadcrumb", javaBreadcrumb, null);
+ _jniExecutor.Run(() =>
+ {
+ using var sentry = GetSentryJava();
+ using var javaBreadcrumb = new AndroidJavaObject("io.sentry.Breadcrumb");
+ javaBreadcrumb.Set("message", breadcrumb.Message);
+ javaBreadcrumb.Set("type", breadcrumb.Type);
+ javaBreadcrumb.Set("category", breadcrumb.Category);
+ using var javaLevel = breadcrumb.Level.ToJavaSentryLevel();
+ javaBreadcrumb.Set("level", javaLevel);
+ sentry.CallStatic("addBreadcrumb", javaBreadcrumb, null);
+ });
}
public override void SetExtraImpl(string key, string? value)
{
- AndroidJNI.AttachCurrentThread();
- using var sentry = GetSentryJava();
- sentry.CallStatic("setExtra", key, value);
+ _jniExecutor.Run(() =>
+ {
+ using var sentry = GetSentryJava();
+ sentry.CallStatic("setExtra", key, value);
+ });
}
public override void SetTagImpl(string key, string value)
{
- AndroidJNI.AttachCurrentThread();
- using var sentry = GetSentryJava();
- sentry.CallStatic("setTag", key, value);
+ _jniExecutor.Run(() =>
+ {
+ using var sentry = GetSentryJava();
+ sentry.CallStatic("setTag", key, value);
+ });
}
public override void UnsetTagImpl(string key)
{
- AndroidJNI.AttachCurrentThread();
- using var sentry = GetSentryJava();
- sentry.CallStatic("removeTag", key);
+ _jniExecutor.Run(() =>
+ {
+ using var sentry = GetSentryJava();
+ sentry.CallStatic("removeTag", key);
+ });
}
public override void SetUserImpl(SentryUser user)
{
- AndroidJNI.AttachCurrentThread();
-
- AndroidJavaObject? javaUser = null;
- try
+ _jniExecutor.Run(() =>
{
- javaUser = new AndroidJavaObject("io.sentry.protocol.User");
- javaUser.Set("email", user.Email);
- javaUser.Set("id", user.Id);
- javaUser.Set("username", user.Username);
- javaUser.Set("ipAddress", user.IpAddress);
- using var sentry = GetSentryJava();
- sentry.CallStatic("setUser", javaUser);
- }
- finally
- {
- javaUser?.Dispose();
- }
+ AndroidJavaObject? javaUser = null;
+ try
+ {
+ javaUser = new AndroidJavaObject("io.sentry.protocol.User");
+ javaUser.Set("email", user.Email);
+ javaUser.Set("id", user.Id);
+ javaUser.Set("username", user.Username);
+ javaUser.Set("ipAddress", user.IpAddress);
+ using var sentry = GetSentryJava();
+ sentry.CallStatic("setUser", javaUser);
+ }
+ finally
+ {
+ javaUser?.Dispose();
+ }
+ });
}
public override void UnsetUserImpl()
{
- AndroidJNI.AttachCurrentThread();
- using var sentry = GetSentryJava();
- sentry.CallStatic("setUser", null);
+ _jniExecutor.Run(() =>
+ {
+ using var sentry = GetSentryJava();
+ sentry.CallStatic("setUser", null);
+ });
}
}
}
diff --git a/src/Sentry.Unity.Android/JniExecutor.cs b/src/Sentry.Unity.Android/JniExecutor.cs
new file mode 100644
index 000000000..39791f910
--- /dev/null
+++ b/src/Sentry.Unity.Android/JniExecutor.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using UnityEngine;
+
+namespace Sentry.Unity.Android
+{
+ public class JniExecutor
+ {
+ private readonly CancellationTokenSource _shutdownSource;
+ private readonly AutoResetEvent _taskEvent;
+ private Delegate _currentTask = null!; // The current task will always be set together with the task event
+
+ private TaskCompletionSource