diff --git a/CHANGELOG.md b/CHANGELOG.md index bda7dd06f9..cc31ee8cf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ * Prevent errors from leaving a self-referencing breadcrumb [#391](https://github.com/bugsnag/bugsnag-android/pull/391) +* Prevent Bugsnag.init from instantiating more than one client + [#403](https://github.com/bugsnag/bugsnag-android/pull/403) + + ## 4.9.3 (2018-11-29) ### Bug fixes diff --git a/features/bugsnag_init.feature b/features/bugsnag_init.feature new file mode 100644 index 0000000000..3c53423a3f --- /dev/null +++ b/features/bugsnag_init.feature @@ -0,0 +1,7 @@ +Feature: Reporting app version + +Scenario: Test handled Android Exception + When I run "BugsnagInitScenario" + Then I should receive a request + And the request is a valid for the error reporting API + And the event "metaData.client.count" equals 1 diff --git a/features/fixtures/mazerunner/src/main/AndroidManifest.xml b/features/fixtures/mazerunner/src/main/AndroidManifest.xml index ef3c038299..62bfe8b769 100644 --- a/features/fixtures/mazerunner/src/main/AndroidManifest.xml +++ b/features/fixtures/mazerunner/src/main/AndroidManifest.xml @@ -12,6 +12,9 @@ + diff --git a/features/fixtures/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/BugsnagInitScenario.kt b/features/fixtures/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/BugsnagInitScenario.kt new file mode 100644 index 0000000000..32c11f9746 --- /dev/null +++ b/features/fixtures/mazerunner/src/main/java/com/bugsnag/android/mazerunner/scenarios/BugsnagInitScenario.kt @@ -0,0 +1,39 @@ +package com.bugsnag.android.mazerunner.scenarios + +import android.content.Context +import com.bugsnag.android.Bugsnag +import com.bugsnag.android.Client +import com.bugsnag.android.Configuration +import java.lang.RuntimeException +import java.util.concurrent.Callable +import java.util.concurrent.Executors + +internal class BugsnagInitScenario( + config: Configuration, + context: Context +) : Scenario(config, context) { + + init { + config.setAutoCaptureSessions(false) + } + + override fun run() { + val threadPool = Executors.newFixedThreadPool(8) + val callables = mutableListOf>() + + IntRange(1, 25).forEach { + callables.add(Callable { Bugsnag.init(context) }) + callables.add(Callable { Bugsnag.init(context, config.apiKey) }) + callables.add(Callable { Bugsnag.init(context, config.apiKey, false) }) + callables.add(Callable { Bugsnag.init(context, Configuration(config.apiKey)) }) + } + + val futures = threadPool.invokeAll(callables) + val uniqueClients = futures.map { it.get() }.distinct() + + val bugsnag = uniqueClients.first()!! + bugsnag.addToTab("client", "count", uniqueClients.size) + bugsnag.notify(RuntimeException()) + } + +} diff --git a/sdk/src/main/java/com/bugsnag/android/Bugsnag.java b/sdk/src/main/java/com/bugsnag/android/Bugsnag.java index b656f37f43..34063c18b9 100644 --- a/sdk/src/main/java/com/bugsnag/android/Bugsnag.java +++ b/sdk/src/main/java/com/bugsnag/android/Bugsnag.java @@ -19,7 +19,8 @@ @SuppressWarnings("checkstyle:JavadocTagContinuationIndentation") public final class Bugsnag { - @Nullable + private static final Object lock = new Object(); + @SuppressLint("StaticFieldLeak") static Client client; @@ -33,9 +34,7 @@ private Bugsnag() { */ @NonNull public static Client init(@NonNull Context androidContext) { - client = new Client(androidContext); - NativeInterface.configureClientObservers(client); - return client; + return init(androidContext, null, true); } /** @@ -46,9 +45,7 @@ public static Client init(@NonNull Context androidContext) { */ @NonNull public static Client init(@NonNull Context androidContext, @Nullable String apiKey) { - client = new Client(androidContext, apiKey); - NativeInterface.configureClientObservers(client); - return client; + return init(androidContext, apiKey, true); } /** @@ -62,9 +59,9 @@ public static Client init(@NonNull Context androidContext, @Nullable String apiK public static Client init(@NonNull Context androidContext, @Nullable String apiKey, boolean enableExceptionHandler) { - client = new Client(androidContext, apiKey, enableExceptionHandler); - NativeInterface.configureClientObservers(client); - return client; + Configuration config + = ConfigFactory.createNewConfiguration(androidContext, apiKey, enableExceptionHandler); + return init(androidContext, config); } /** @@ -75,11 +72,23 @@ public static Client init(@NonNull Context androidContext, */ @NonNull public static Client init(@NonNull Context androidContext, @NonNull Configuration config) { - client = new Client(androidContext, config); - NativeInterface.configureClientObservers(client); + synchronized (lock) { + if (client == null) { + client = new Client(androidContext, config); + NativeInterface.configureClientObservers(client); + } else { + logClientInitWarning(); + } + } return client; } + private static void logClientInitWarning() { + Logger.warn("It appears that Bugsnag.init() was called more than once. Subsequent " + + "calls have no effect, but may indicate that Bugsnag is not integrated in an" + + " Application subclass, which can lead to undesired behaviour."); + } + /** * Set the application version sent to Bugsnag. By default we'll pull this * from your AndroidManifest.xml