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() } } }