diff --git a/bugsnag-android-core/detekt-baseline.xml b/bugsnag-android-core/detekt-baseline.xml
index 4517155a90..03e8d54156 100644
--- a/bugsnag-android-core/detekt-baseline.xml
+++ b/bugsnag-android-core/detekt-baseline.xml
@@ -7,7 +7,7 @@
LongParameterList:AppDataCollector.kt$AppDataCollector$( appContext: Context, private val packageManager: PackageManager?, private val config: ImmutableConfig, private val sessionTracker: SessionTracker, private val activityManager: ActivityManager?, private val launchCrashTracker: LaunchCrashTracker, private val memoryTrimState: MemoryTrimState )
LongParameterList:AppWithState.kt$AppWithState$( binaryArch: String?, id: String?, releaseStage: String?, version: String?, codeBundleId: String?, buildUuid: String?, type: String?, versionCode: Number?, /** * The number of milliseconds the application was running before the event occurred */ var duration: Number?, /** * The number of milliseconds the application was running in the foreground before the * event occurred */ var durationInForeground: Number?, /** * Whether the application was in the foreground when the event occurred */ var inForeground: Boolean?, /** * Whether the application was launching when the event occurred */ var isLaunching: Boolean? )
LongParameterList:AppWithState.kt$AppWithState$( config: ImmutableConfig, binaryArch: String?, id: String?, releaseStage: String?, version: String?, codeBundleId: String?, duration: Number?, durationInForeground: Number?, inForeground: Boolean?, isLaunching: Boolean? )
- LongParameterList:DataCollectionModule.kt$DataCollectionModule$( contextModule: ContextModule, configModule: ConfigModule, systemServiceModule: SystemServiceModule, trackerModule: TrackerModule, private val bgTaskService: BackgroundTaskService, private val connectivity: Connectivity, private val deviceId: String?, memoryTrimState: MemoryTrimState )
+ LongParameterList:DataCollectionModule.kt$DataCollectionModule$( contextModule: ContextModule, configModule: ConfigModule, systemServiceModule: SystemServiceModule, trackerModule: TrackerModule, bgTaskService: BackgroundTaskService, connectivity: Connectivity, deviceId: String?, memoryTrimState: MemoryTrimState )
LongParameterList:Device.kt$Device$( buildInfo: DeviceBuildInfo, /** * The Application Binary Interface used */ var cpuAbi: Array<String>?, /** * Whether the device has been jailbroken */ var jailbroken: Boolean?, /** * A UUID generated by Bugsnag and used for the individual application on a device */ var id: String?, /** * The IETF language tag of the locale used */ var locale: String?, /** * The total number of bytes of memory on the device */ var totalMemory: Long?, /** * A collection of names and their versions of the primary languages, frameworks or * runtimes that the application is running on */ runtimeVersions: MutableMap<String, Any>? )
LongParameterList:DeviceBuildInfo.kt$DeviceBuildInfo$( val manufacturer: String?, val model: String?, val osVersion: String?, val apiLevel: Int?, val osBuild: String?, val fingerprint: String?, val tags: String?, val brand: String?, val cpuAbis: Array<String>? )
LongParameterList:DeviceDataCollector.kt$DeviceDataCollector$( private val connectivity: Connectivity, private val appContext: Context, resources: Resources, private val deviceId: String?, private val buildInfo: DeviceBuildInfo, private val dataDirectory: File, rootDetector: RootDetector, private val bgTaskService: BackgroundTaskService, private val logger: Logger )
@@ -34,7 +34,6 @@
SwallowedException:BugsnagEventMapper.kt$BugsnagEventMapper$catch (pe: IllegalArgumentException) { ndkDateFormatHolder.get()!!.parse(this) ?: throw IllegalArgumentException("cannot parse date $this") }
SwallowedException:ConnectivityCompat.kt$ConnectivityLegacy$catch (e: NullPointerException) { // in some rare cases we get a remote NullPointerException via Parcel.readException null }
SwallowedException:ContextExtensions.kt$catch (exc: RuntimeException) { null }
- SwallowedException:DependencyModule.kt$DependencyModule$catch (exception: Exception) { // ignore failures }
SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$catch (exc: Exception) { false }
SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$catch (exception: Exception) { logger.w("Could not get battery status") }
SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$catch (exception: Exception) { logger.w("Could not get locationStatus") }
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/DataCollectionModule.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/DataCollectionModule.kt
index 02aa58c23a..577b9bf7bc 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/DataCollectionModule.kt
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/DataCollectionModule.kt
@@ -15,9 +15,9 @@ internal class DataCollectionModule(
configModule: ConfigModule,
systemServiceModule: SystemServiceModule,
trackerModule: TrackerModule,
- private val bgTaskService: BackgroundTaskService,
- private val connectivity: Connectivity,
- private val deviceId: String?,
+ bgTaskService: BackgroundTaskService,
+ connectivity: Connectivity,
+ deviceId: String?,
memoryTrimState: MemoryTrimState
) : DependencyModule() {
@@ -27,25 +27,24 @@ internal class DataCollectionModule(
private val deviceBuildInfo: DeviceBuildInfo = DeviceBuildInfo.defaultInfo()
private val dataDir = Environment.getDataDirectory()
- val appDataCollector = AppDataCollector(
- ctx,
- ctx.packageManager,
- cfg,
- trackerModule.sessionTracker,
- systemServiceModule.activityManager,
- trackerModule.launchCrashTracker,
- memoryTrimState
- )
-
- private lateinit var rootDetector: RootDetector
- private lateinit var _deviceDataCollector: DeviceDataCollector
+ val appDataCollector by future {
+ AppDataCollector(
+ ctx,
+ ctx.packageManager,
+ cfg,
+ trackerModule.sessionTracker,
+ systemServiceModule.activityManager,
+ trackerModule.launchCrashTracker,
+ memoryTrimState
+ )
+ }
- val deviceDataCollector: DeviceDataCollector
- get() = resolvedValueOf { _deviceDataCollector }
+ private val rootDetector by future {
+ RootDetector(logger = logger, deviceBuildInfo = deviceBuildInfo)
+ }
- override fun resolveDependencies() {
- rootDetector = RootDetector(logger = logger, deviceBuildInfo = deviceBuildInfo)
- _deviceDataCollector = DeviceDataCollector(
+ val deviceDataCollector by future {
+ DeviceDataCollector(
connectivity,
ctx,
ctx.resources,
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStorageModule.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStorageModule.kt
index aeace4fce0..78980d1458 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStorageModule.kt
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/EventStorageModule.kt
@@ -21,24 +21,19 @@ internal class EventStorageModule(
private val cfg = configModule.config
- private val delegate = InternalReportDelegate(
- contextModule.ctx,
- cfg.logger,
- cfg,
- systemServiceModule.storageManager,
- dataCollectionModule.appDataCollector,
- dataCollectionModule.deviceDataCollector,
- trackerModule.sessionTracker,
- notifier,
- bgTaskService
- )
+ private val delegate by future {
+ InternalReportDelegate(
+ contextModule.ctx,
+ cfg.logger,
+ cfg,
+ systemServiceModule.storageManager,
+ dataCollectionModule.appDataCollector,
+ dataCollectionModule.deviceDataCollector,
+ trackerModule.sessionTracker,
+ notifier,
+ bgTaskService
+ )
+ }
- val eventStore = EventStore(
- cfg,
- cfg.logger,
- notifier,
- bgTaskService,
- delegate,
- callbackState
- )
+ val eventStore by future { EventStore(cfg, cfg.logger, notifier, bgTaskService, delegate, callbackState) }
}
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt
index cc97f91e43..2d4bff14ee 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/StorageModule.kt
@@ -8,57 +8,40 @@ import com.bugsnag.android.internal.dag.DependencyModule
* A dependency module which constructs the objects that store information to disk in Bugsnag.
*/
internal class StorageModule(
- private val appContext: Context,
- private val immutableConfig: ImmutableConfig,
- private val logger: Logger
+ appContext: Context,
+ immutableConfig: ImmutableConfig,
+ logger: Logger
) : DependencyModule() {
- private lateinit var _sharedPrefMigrator: SharedPrefMigrator
- private lateinit var _userStore: UserStore
- private lateinit var _lastRunInfoStore: LastRunInfoStore
- private lateinit var _sessionStore: SessionStore
+ val sharedPrefMigrator by future { SharedPrefMigrator(appContext) }
- private var _deviceId: String? = null
- private var _lastRunInfo: LastRunInfo? = null
-
- val sharedPrefMigrator get() = resolvedValueOf { _sharedPrefMigrator }
- val deviceId get() = resolvedValueOf { _deviceId }
- val userStore get() = resolvedValueOf { _userStore }
- val lastRunInfoStore get() = resolvedValueOf { _lastRunInfoStore }
- val sessionStore get() = resolvedValueOf { _sessionStore }
- val lastRunInfo get() = resolvedValueOf { _lastRunInfo }
+ private val deviceIdStore by future {
+ DeviceIdStore(
+ appContext,
+ sharedPrefMigrator = sharedPrefMigrator,
+ logger = logger
+ )
+ }
- override fun resolveDependencies() {
- _sharedPrefMigrator = SharedPrefMigrator(appContext)
- _deviceId = resolveDeviceId()
+ val deviceId by future { deviceIdStore.loadDeviceId() }
- _userStore = UserStore(
+ val userStore by future {
+ UserStore(
immutableConfig,
- _deviceId,
- sharedPrefMigrator = _sharedPrefMigrator,
+ deviceId,
+ sharedPrefMigrator = sharedPrefMigrator,
logger = logger
)
-
- _lastRunInfoStore = LastRunInfoStore(immutableConfig)
- _sessionStore = SessionStore(immutableConfig, logger, null)
-
- _lastRunInfo = resolveLastRunInfo()
}
- private fun resolveDeviceId(): String? {
- val deviceIdStore = DeviceIdStore(
- appContext,
- sharedPrefMigrator = _sharedPrefMigrator,
- logger = logger
- )
+ val lastRunInfoStore by future { LastRunInfoStore(immutableConfig) }
- return deviceIdStore.loadDeviceId()
- }
+ val sessionStore by future { SessionStore(immutableConfig, logger, null) }
- private fun resolveLastRunInfo(): LastRunInfo? {
+ val lastRunInfo by future {
val info = lastRunInfoStore.load()
val currentRunInfo = LastRunInfo(0, crashed = false, crashedDuringLaunch = false)
lastRunInfoStore.persist(currentRunInfo)
- return info
+ info
}
}
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/DependencyModule.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/DependencyModule.kt
index bdb39c9b3e..f766b0dd2c 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/DependencyModule.kt
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/internal/dag/DependencyModule.kt
@@ -1,34 +1,22 @@
package com.bugsnag.android.internal.dag
-import androidx.annotation.WorkerThread
import com.bugsnag.android.BackgroundTaskService
import com.bugsnag.android.TaskType
-import java.util.concurrent.Callable
internal abstract class DependencyModule {
- @Volatile
- internal var dependenciesResolved = false
+ private val properties = mutableListOf>()
- inline fun resolvedValueOf(value: () -> R): R {
- synchronized(this) {
- while (!dependenciesResolved) {
- // The probability that we actually need to wait for the dependencies to be resolved
- // is quite low, so we don't want the overhead (especially during startup) or a
- // ReentrantLock. Instead we want to use the Java wait() and notify() methods
- // so we can leverage monitor locks, which (at time of writing) typically have
- // no allocation cost (until there is contention)
- // https://android.googlesource.com/platform/art/+/master/runtime/monitor.cc#57
- @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
- (this as Object).wait()
- }
+ /**
+ * Creates a new [Lazy] property that is marked as an object that should be resolved off the
+ * main thread when [resolveDependencies] is called.
+ */
+ fun future(initializer: () -> T): Lazy {
+ val lazy = lazy {
+ initializer()
}
-
- return value()
- }
-
- @WorkerThread
- protected open fun resolveDependencies() {
+ properties.add(lazy)
+ return lazy
}
/**
@@ -36,23 +24,14 @@ internal abstract class DependencyModule {
* for modules to construct objects in a background thread, then have a user block on another
* thread until all the objects have been constructed.
*/
- open fun resolveDependencies(bgTaskService: BackgroundTaskService, taskType: TaskType) {
- try {
+ fun resolveDependencies(bgTaskService: BackgroundTaskService, taskType: TaskType) {
+ kotlin.runCatching {
bgTaskService.submitTask(
taskType,
- // Callable avoids wrapping the Runnable in a Callable
- Callable {
- synchronized(this) {
- dependenciesResolved = true
- resolveDependencies()
-
- @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
- (this as Object).notifyAll()
- }
+ Runnable {
+ properties.forEach { it.value }
}
- )
- } catch (exception: Exception) {
- // ignore failures
+ ).get()
}
}
}