Skip to content

Commit 75ae950

Browse files
Making Services closeable (#1196)
* Creating service contract * Implementing close method * Closing Services * Closing services on rum shutdown * Using getter val * apiDump * Update services/src/test/java/io/opentelemetry/android/internal/services/visiblescreen/VisibleScreenTrackerTest.java Co-authored-by: jason plumb <[email protected]> * Verifying services.close * ApiDump --------- Co-authored-by: jason plumb <[email protected]>
1 parent 1d39244 commit 75ae950

File tree

16 files changed

+196
-52
lines changed

16 files changed

+196
-52
lines changed

core/src/main/java/io/opentelemetry/android/OpenTelemetryRumBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ public OpenTelemetryRum build() {
337337
if (exportScheduleHandler != null) {
338338
exportScheduleHandler.disable();
339339
}
340+
services.close();
340341
});
341342

342343
// AsyncTask is deprecated but the thread pool is still used all over the Android SDK

services/api/services.api

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
public class io/opentelemetry/android/internal/services/CacheStorage {
1+
public final class io/opentelemetry/android/internal/services/CacheStorage : io/opentelemetry/android/internal/services/Service {
22
public fun <init> (Landroid/content/Context;)V
3-
public fun getCacheDir ()Ljava/io/File;
3+
public final fun getCacheDir ()Ljava/io/File;
44
}
55

6-
public final class io/opentelemetry/android/internal/services/Services {
6+
public abstract interface class io/opentelemetry/android/internal/services/Service : java/io/Closeable {
7+
public fun close ()V
8+
}
9+
10+
public final class io/opentelemetry/android/internal/services/Services : java/io/Closeable {
711
public static final field Companion Lio/opentelemetry/android/internal/services/Services$Companion;
12+
public fun close ()V
813
public static final fun get (Landroid/app/Application;)Lio/opentelemetry/android/internal/services/Services;
914
public final fun getAppLifecycle ()Lio/opentelemetry/android/internal/services/applifecycle/AppLifecycle;
1015
public final fun getCacheStorage ()Lio/opentelemetry/android/internal/services/CacheStorage;
@@ -19,7 +24,8 @@ public final class io/opentelemetry/android/internal/services/Services$Companion
1924
public final fun set (Lio/opentelemetry/android/internal/services/Services;)V
2025
}
2126

22-
public final class io/opentelemetry/android/internal/services/applifecycle/AppLifecycle {
27+
public final class io/opentelemetry/android/internal/services/applifecycle/AppLifecycle : io/opentelemetry/android/internal/services/Service {
28+
public fun close ()V
2329
public final fun registerListener (Lio/opentelemetry/android/internal/services/applifecycle/ApplicationStateListener;)V
2430
}
2531

@@ -33,11 +39,12 @@ public class io/opentelemetry/android/internal/services/network/CarrierFinder {
3339
public fun get ()Lio/opentelemetry/android/common/internal/features/networkattributes/data/Carrier;
3440
}
3541

36-
public final class io/opentelemetry/android/internal/services/network/CurrentNetworkProvider {
42+
public final class io/opentelemetry/android/internal/services/network/CurrentNetworkProvider : io/opentelemetry/android/internal/services/Service {
3743
public static final field NO_NETWORK Lio/opentelemetry/android/common/internal/features/networkattributes/data/CurrentNetwork;
3844
public static final field UNKNOWN_NETWORK Lio/opentelemetry/android/common/internal/features/networkattributes/data/CurrentNetwork;
3945
public fun <init> (Lio/opentelemetry/android/internal/services/network/detector/NetworkDetector;Landroid/net/ConnectivityManager;)V
4046
public fun addNetworkChangeListener (Lio/opentelemetry/android/internal/services/network/NetworkChangeListener;)V
47+
public fun close ()V
4148
public fun getCurrentNetwork ()Lio/opentelemetry/android/common/internal/features/networkattributes/data/CurrentNetwork;
4249
public fun refreshNetworkStatus ()Lio/opentelemetry/android/common/internal/features/networkattributes/data/CurrentNetwork;
4350
}
@@ -64,13 +71,15 @@ public abstract class io/opentelemetry/android/internal/services/periodicwork/Pe
6471
public abstract fun shouldStopRunning ()Z
6572
}
6673

67-
public final class io/opentelemetry/android/internal/services/periodicwork/PeriodicWork {
74+
public final class io/opentelemetry/android/internal/services/periodicwork/PeriodicWork : io/opentelemetry/android/internal/services/Service {
75+
public fun close ()V
6876
public final fun enqueue (Ljava/lang/Runnable;)V
6977
}
7078

71-
public final class io/opentelemetry/android/internal/services/visiblescreen/VisibleScreenTracker {
79+
public final class io/opentelemetry/android/internal/services/visiblescreen/VisibleScreenTracker : io/opentelemetry/android/internal/services/Service {
7280
public final fun activityPaused (Landroid/app/Activity;)V
7381
public final fun activityResumed (Landroid/app/Activity;)V
82+
public fun close ()V
7483
public final fun fragmentPaused (Landroidx/fragment/app/Fragment;)V
7584
public final fun fragmentResumed (Landroidx/fragment/app/Fragment;)V
7685
public final fun getCurrentlyVisibleScreen ()Ljava/lang/String;

services/src/main/java/io/opentelemetry/android/internal/services/CacheStorage.java

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.android.internal.services
7+
8+
import android.content.Context
9+
import java.io.File
10+
11+
/**
12+
* Utility to get information about the host app.
13+
*
14+
* This class is internal and not for public use. Its APIs are unstable and can change at any
15+
* time.
16+
*/
17+
class CacheStorage(
18+
private val appContext: Context,
19+
) : Service {
20+
val cacheDir: File
21+
get() = appContext.cacheDir
22+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.android.internal.services
7+
8+
import java.io.Closeable
9+
10+
interface Service : Closeable {
11+
override fun close() {}
12+
}

services/src/main/java/io/opentelemetry/android/internal/services/Services.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ import io.opentelemetry.android.internal.services.network.CurrentNetworkProvider
1515
import io.opentelemetry.android.internal.services.network.detector.NetworkDetector
1616
import io.opentelemetry.android.internal.services.periodicwork.PeriodicWork
1717
import io.opentelemetry.android.internal.services.visiblescreen.VisibleScreenTracker
18+
import java.io.Closeable
1819

1920
/**
2021
* This class is internal and not for public use. Its APIs are unstable and can change at any time.
2122
*/
2223
class Services internal constructor(
2324
private val factory: ServicesFactory,
24-
) {
25+
) : Closeable {
2526
val cacheStorage: CacheStorage by lazy {
2627
factory.createCacheStorage()
2728
}
@@ -42,6 +43,14 @@ class Services internal constructor(
4243
factory.createVisibleScreenTracker()
4344
}
4445

46+
override fun close() {
47+
cacheStorage.close()
48+
periodicWork.close()
49+
currentNetworkProvider.close()
50+
appLifecycle.close()
51+
visibleScreenTracker.close()
52+
}
53+
4554
companion object {
4655
private var instance: Services? = null
4756

services/src/main/java/io/opentelemetry/android/internal/services/applifecycle/AppLifecycle.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,25 @@
66
package io.opentelemetry.android.internal.services.applifecycle
77

88
import androidx.lifecycle.Lifecycle
9+
import io.opentelemetry.android.internal.services.Service
910

1011
/**
1112
* This class is internal and not for public use. Its APIs are unstable and can change at any time.
1213
*/
1314
class AppLifecycle internal constructor(
1415
private val applicationStateWatcher: ApplicationStateWatcher,
15-
appLifecycle: Lifecycle,
16-
) {
16+
private val appLifecycle: Lifecycle,
17+
) : Service {
1718
init {
1819
appLifecycle.addObserver(applicationStateWatcher)
1920
}
2021

2122
fun registerListener(listener: ApplicationStateListener) {
2223
applicationStateWatcher.registerListener(listener)
2324
}
25+
26+
override fun close() {
27+
appLifecycle.removeObserver(applicationStateWatcher)
28+
applicationStateWatcher.close()
29+
}
2430
}

services/src/main/java/io/opentelemetry/android/internal/services/applifecycle/ApplicationStateWatcher.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ package io.opentelemetry.android.internal.services.applifecycle
77

88
import androidx.lifecycle.DefaultLifecycleObserver
99
import androidx.lifecycle.LifecycleOwner
10+
import java.io.Closeable
1011
import java.util.concurrent.CopyOnWriteArrayList
1112

1213
/**
1314
* This class is internal and not for public use. Its APIs are unstable and can change at any time.
1415
*/
15-
internal class ApplicationStateWatcher : DefaultLifecycleObserver {
16+
internal class ApplicationStateWatcher :
17+
DefaultLifecycleObserver,
18+
Closeable {
1619
private val applicationStateListeners: MutableList<ApplicationStateListener> =
1720
CopyOnWriteArrayList()
1821

@@ -31,4 +34,8 @@ internal class ApplicationStateWatcher : DefaultLifecycleObserver {
3134
fun registerListener(listener: ApplicationStateListener) {
3235
applicationStateListeners.add(listener)
3336
}
37+
38+
override fun close() {
39+
applicationStateListeners.clear()
40+
}
3441
}

services/src/main/java/io/opentelemetry/android/internal/services/network/CurrentNetworkProvider.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
import io.opentelemetry.android.common.RumConstants;
1717
import io.opentelemetry.android.common.internal.features.networkattributes.data.CurrentNetwork;
1818
import io.opentelemetry.android.common.internal.features.networkattributes.data.NetworkState;
19+
import io.opentelemetry.android.internal.services.Service;
1920
import io.opentelemetry.android.internal.services.network.detector.NetworkDetector;
2021
import java.util.List;
2122
import java.util.concurrent.CopyOnWriteArrayList;
23+
import java.util.concurrent.atomic.AtomicReference;
2224
import java.util.function.Supplier;
2325

2426
// note: based on ideas from stack overflow:
@@ -31,7 +33,7 @@
3133
* <p>This class is internal and not for public use. Its APIs are unstable and can change at any
3234
* time.
3335
*/
34-
public final class CurrentNetworkProvider {
36+
public final class CurrentNetworkProvider implements Service {
3537

3638
public static final CurrentNetwork NO_NETWORK =
3739
CurrentNetwork.builder(NetworkState.NO_NETWORK_AVAILABLE).build();
@@ -42,6 +44,8 @@ public final class CurrentNetworkProvider {
4244
private final ConnectivityManager connectivityManager;
4345

4446
private volatile CurrentNetwork currentNetwork = UNKNOWN_NETWORK;
47+
private final AtomicReference<ConnectivityManager.NetworkCallback> callbackRef =
48+
new AtomicReference<>();
4549
private final List<NetworkChangeListener> listeners = new CopyOnWriteArrayList<>();
4650

4751
public CurrentNetworkProvider(
@@ -79,13 +83,17 @@ private void registerNetworkCallbacks(Supplier<NetworkRequest> createNetworkMoni
7983
registerNetworkCallbackApi24();
8084
} else {
8185
NetworkRequest networkRequest = createNetworkMonitoringRequest.get();
82-
connectivityManager.registerNetworkCallback(networkRequest, new ConnectionMonitor());
86+
ConnectivityManager.NetworkCallback callback = new ConnectionMonitor();
87+
connectivityManager.registerNetworkCallback(networkRequest, callback);
88+
callbackRef.set(callback);
8389
}
8490
}
8591

8692
@RequiresApi(Build.VERSION_CODES.N)
8793
private void registerNetworkCallbackApi24() {
88-
connectivityManager.registerDefaultNetworkCallback(new ConnectionMonitor());
94+
ConnectivityManager.NetworkCallback callback = new ConnectionMonitor();
95+
connectivityManager.registerDefaultNetworkCallback(callback);
96+
callbackRef.set(callback);
8997
}
9098

9199
/** Returns up-to-date {@linkplain CurrentNetwork current network information}. */
@@ -125,6 +133,16 @@ private void notifyListeners(CurrentNetwork activeNetwork) {
125133
}
126134
}
127135

136+
@Override
137+
public void close() {
138+
ConnectivityManager.NetworkCallback callback = callbackRef.get();
139+
if (callback != null) {
140+
connectivityManager.unregisterNetworkCallback(callback);
141+
listeners.clear();
142+
callbackRef.set(null);
143+
}
144+
}
145+
128146
private final class ConnectionMonitor extends ConnectivityManager.NetworkCallback {
129147

130148
@Override

services/src/main/java/io/opentelemetry/android/internal/services/periodicwork/PeriodicWork.kt

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@ package io.opentelemetry.android.internal.services.periodicwork
77

88
import android.os.Handler
99
import android.os.Looper
10+
import io.opentelemetry.android.internal.services.Service
11+
import java.io.Closeable
1012
import java.util.concurrent.ConcurrentLinkedQueue
1113
import java.util.concurrent.LinkedBlockingQueue
1214
import java.util.concurrent.ThreadPoolExecutor
1315
import java.util.concurrent.TimeUnit
16+
import java.util.concurrent.atomic.AtomicBoolean
1417

1518
/**
1619
* Utility to run periodic background work.
1720
*
1821
* <p>This class is internal and not for public use. Its APIs are unstable and can change at any
1922
* time.
2023
*/
21-
class PeriodicWork internal constructor() {
24+
class PeriodicWork internal constructor() : Service {
2225
private val delegator = WorkerDelegator()
2326

2427
init {
@@ -29,14 +32,21 @@ class PeriodicWork internal constructor() {
2932
delegator.enqueue(runnable)
3033
}
3134

32-
private class WorkerDelegator : Runnable {
35+
override fun close() {
36+
delegator.close()
37+
}
38+
39+
private class WorkerDelegator :
40+
Runnable,
41+
Closeable {
3342
companion object {
3443
private const val SECONDS_TO_KILL_IDLE_THREADS = 30L
3544
private const val SECONDS_FOR_NEXT_LOOP = 10L
3645
private const val MAX_AMOUNT_OF_WORKER_THREADS = 1
3746
private const val NUMBER_OF_PERMANENT_WORKER_THREADS = 0
3847
}
3948

49+
private val closed = AtomicBoolean(false)
4050
private val queue = ConcurrentLinkedQueue<Runnable>()
4151
private val handler = Handler(Looper.getMainLooper())
4252
private val executor =
@@ -49,14 +59,26 @@ class PeriodicWork internal constructor() {
4959
)
5060

5161
fun enqueue(runnable: Runnable) {
62+
if (closed.get()) {
63+
return
64+
}
5265
queue.add(runnable)
5366
}
5467

5568
override fun run() {
69+
if (closed.get()) {
70+
return
71+
}
5672
delegateToWorkerThread()
5773
scheduleNextLookUp()
5874
}
5975

76+
override fun close() {
77+
if (!closed.compareAndSet(false, true)) {
78+
queue.clear()
79+
}
80+
}
81+
6082
private fun delegateToWorkerThread() {
6183
while (queue.isNotEmpty()) {
6284
executor.execute(queue.poll())

0 commit comments

Comments
 (0)