11package io .sentry .android .core ;
22
3+ import io .sentry .IConnectionStatusProvider ;
34import io .sentry .IHub ;
45import io .sentry .Integration ;
56import io .sentry .SendCachedEnvelopeFireAndForgetIntegration ;
67import io .sentry .SentryLevel ;
78import io .sentry .SentryOptions ;
89import io .sentry .util .LazyEvaluator ;
910import io .sentry .util .Objects ;
11+ import java .io .Closeable ;
12+ import java .io .IOException ;
1013import java .util .concurrent .Future ;
1114import java .util .concurrent .RejectedExecutionException ;
1215import java .util .concurrent .TimeUnit ;
1316import java .util .concurrent .TimeoutException ;
17+ import java .util .concurrent .atomic .AtomicBoolean ;
1418import org .jetbrains .annotations .NotNull ;
19+ import org .jetbrains .annotations .Nullable ;
1520
16- final class SendCachedEnvelopeIntegration implements Integration {
21+ final class SendCachedEnvelopeIntegration
22+ implements Integration , IConnectionStatusProvider .IConnectionStatusObserver , Closeable {
1723
1824 private final @ NotNull SendCachedEnvelopeFireAndForgetIntegration .SendFireAndForgetFactory
1925 factory ;
2026 private final @ NotNull LazyEvaluator <Boolean > startupCrashMarkerEvaluator ;
27+ private final AtomicBoolean startupCrashHandled = new AtomicBoolean (false );
28+ private @ Nullable IConnectionStatusProvider connectionStatusProvider ;
29+ private @ Nullable IHub hub ;
30+ private @ Nullable SentryAndroidOptions options ;
2131
2232 public SendCachedEnvelopeIntegration (
2333 final @ NotNull SendCachedEnvelopeFireAndForgetIntegration .SendFireAndForgetFactory factory ,
@@ -29,7 +39,8 @@ public SendCachedEnvelopeIntegration(
2939 @ Override
3040 public void register (@ NotNull IHub hub , @ NotNull SentryOptions options ) {
3141 Objects .requireNonNull (hub , "Hub is required" );
32- final SentryAndroidOptions androidOptions =
42+ this .hub = hub ;
43+ this .options =
3344 Objects .requireNonNull (
3445 (options instanceof SentryAndroidOptions ) ? (SentryAndroidOptions ) options : null ,
3546 "SentryAndroidOptions is required" );
@@ -40,51 +51,74 @@ public void register(@NotNull IHub hub, @NotNull SentryOptions options) {
4051 return ;
4152 }
4253
54+ connectionStatusProvider = options .getConnectionStatusProvider ();
55+ connectionStatusProvider .addConnectionStatusObserver (this );
56+
57+ sendCachedEnvelopes (hub , this .options );
58+ }
59+
60+ @ Override
61+ public void close () throws IOException {
62+ if (connectionStatusProvider != null ) {
63+ connectionStatusProvider .removeConnectionStatusObserver (this );
64+ }
65+ }
66+
67+ @ Override
68+ public void onConnectionStatusChanged (IConnectionStatusProvider .ConnectionStatus status ) {
69+ if (hub != null && options != null ) {
70+ sendCachedEnvelopes (hub , options );
71+ }
72+ }
73+
74+ private synchronized void sendCachedEnvelopes (
75+ final @ NotNull IHub hub , final @ NotNull SentryAndroidOptions options ) {
76+
4377 final SendCachedEnvelopeFireAndForgetIntegration .SendFireAndForget sender =
44- factory .create (hub , androidOptions );
78+ factory .create (hub , options );
4579
4680 if (sender == null ) {
47- androidOptions .getLogger ().log (SentryLevel .ERROR , "SendFireAndForget factory is null." );
81+ options .getLogger ().log (SentryLevel .ERROR , "SendFireAndForget factory is null." );
4882 return ;
4983 }
50-
5184 try {
52- Future <?> future =
53- androidOptions
85+ final Future <?> future =
86+ options
5487 .getExecutorService ()
5588 .submit (
5689 () -> {
5790 try {
5891 sender .send ();
5992 } catch (Throwable e ) {
60- androidOptions
93+ options
6194 .getLogger ()
6295 .log (SentryLevel .ERROR , "Failed trying to send cached events." , e );
6396 }
6497 });
6598
66- if (startupCrashMarkerEvaluator .getValue ()) {
67- androidOptions
68- .getLogger ()
69- .log (SentryLevel .DEBUG , "Startup Crash marker exists, blocking flush." );
99+ // startupCrashMarkerEvaluator remains true on subsequent runs, let's ensure we only block on
100+ // the very first execution (=app start)
101+ if (startupCrashMarkerEvaluator .getValue ()
102+ && startupCrashHandled .compareAndSet (false , true )) {
103+ options .getLogger ().log (SentryLevel .DEBUG , "Startup Crash marker exists, blocking flush." );
70104 try {
71- future .get (androidOptions .getStartupCrashFlushTimeoutMillis (), TimeUnit .MILLISECONDS );
105+ future .get (options .getStartupCrashFlushTimeoutMillis (), TimeUnit .MILLISECONDS );
72106 } catch (TimeoutException e ) {
73- androidOptions
107+ options
74108 .getLogger ()
75109 .log (SentryLevel .DEBUG , "Synchronous send timed out, continuing in the background." );
76110 }
77111 }
78- androidOptions .getLogger ().log (SentryLevel .DEBUG , "SendCachedEnvelopeIntegration installed." );
112+ options .getLogger ().log (SentryLevel .DEBUG , "SendCachedEnvelopeIntegration installed." );
79113 } catch (RejectedExecutionException e ) {
80- androidOptions
114+ options
81115 .getLogger ()
82116 .log (
83117 SentryLevel .ERROR ,
84118 "Failed to call the executor. Cached events will not be sent. Did you call Sentry.close()?" ,
85119 e );
86120 } catch (Throwable e ) {
87- androidOptions
121+ options
88122 .getLogger ()
89123 .log (SentryLevel .ERROR , "Failed to call the executor. Cached events will not be sent" , e );
90124 }
0 commit comments