3
3
import android .content .Context ;
4
4
import android .util .LongSparseArray ;
5
5
6
+ import java .util .concurrent .atomic .AtomicLong ;
7
+
6
8
import io .ably .lib .push .Push ;
7
9
import io .ably .lib .push .PushChannel ;
8
10
import io .ably .lib .realtime .AblyRealtime ;
23
25
* client by calling @{AblyInstanceStore#getRealtime(final long handle)}.
24
26
*/
25
27
class AblyInstanceStore {
26
-
27
28
private static final AblyInstanceStore instance = new AblyInstanceStore ();
28
- private long nextHandle = 1 ;
29
29
30
30
// Android Studio warns against using HashMap with integer keys, and
31
31
// suggests using LongSparseArray. More information at https://stackoverflow.com/a/31413003
@@ -34,38 +34,100 @@ class AblyInstanceStore {
34
34
private final LongSparseArray <AblyRest > restInstances = new LongSparseArray <>();
35
35
private final LongSparseArray <AblyRealtime > realtimeInstances = new LongSparseArray <>();
36
36
private final LongSparseArray <AsyncPaginatedResult <Object >> paginatedResults = new LongSparseArray <>();
37
+ private final AtomicLong nextHandle = new AtomicLong (1 );
37
38
38
39
static synchronized AblyInstanceStore getInstance () {
39
40
return instance ;
40
41
}
41
42
42
43
/**
43
- * Returns a handle representing the next client that will be created. This handle can be used
44
- * to get the client **after** it is instantiated using [createRest] or [createRealtime].
44
+ * A reserved client handle. Safe to be used from any thread.
45
+ *
46
+ * Instances support the creation of a single Rest or Realtime instance, where only one of the
47
+ * create methods may be called and it may only be called once.
45
48
*/
46
- long getHandleForNextClient () {
47
- return nextHandle ;
49
+ interface ClientHandle {
50
+ /**
51
+ * Get the handle that will be used to store this client when it is created, or the handle
52
+ * that was used to store it when it was created.
53
+ * This property may be read at any time, from any thread.
54
+ */
55
+ long getHandle ();
56
+
57
+ /**
58
+ * Create an {@link AblyRest} instance and store it using this handle.
59
+ * @param clientOptions The Ably client options for the new Rest instance.
60
+ * @param applicationContext The Android application context to supply to the new Rest
61
+ * instance using its {@link AblyRest#setAndroidContext(Context)} method.
62
+ * @return The handle used to store the instance. Same as {@link #getHandle()}.
63
+ * @throws IllegalStateException If this handle has already been used to create a Rest or
64
+ * Realtime instance.
65
+ * @throws AblyException If the {@link AblyRest} instance creation failed.
66
+ */
67
+ long createRest (ClientOptions clientOptions , Context applicationContext ) throws AblyException ;
68
+
69
+ /**
70
+ * Create an {@link AblyRealtime} instance and store it using this handle.
71
+ * @param clientOptions The Ably client options for the new Realtime instance.
72
+ * @param applicationContext The Android application context to supply to the new Realtime
73
+ * instance using its {@link AblyRealtime#setAndroidContext(Context)} method.
74
+ * @return The handle used to store the instance. Same as {@link #getHandle()}.
75
+ * @throws IllegalStateException If this handle has already been used to create a Rest or
76
+ * Realtime instance.
77
+ * @throws AblyException If the {@link AblyRealtime} instance creation failed.
78
+ */
79
+ long createRealtime (ClientOptions clientOptions , Context applicationContext ) throws AblyException ;
48
80
}
49
81
50
- long createRest (final ClientOptions clientOptions , Context applicationContext ) throws AblyException {
51
- final AblyRest rest = new AblyRest (clientOptions );
52
- rest .setAndroidContext (applicationContext );
53
- restInstances .put (nextHandle , rest );
54
- return nextHandle ++;
82
+ private class ReservedClientHandle implements ClientHandle {
83
+ private final long handle ;
84
+ private volatile boolean used = false ;
85
+
86
+ ReservedClientHandle (final long handle ) {
87
+ this .handle = handle ;
88
+ }
89
+
90
+ @ Override
91
+ public long getHandle () {
92
+ return handle ;
93
+ }
94
+
95
+ @ Override
96
+ public synchronized long createRest (final ClientOptions clientOptions , final Context applicationContext ) throws AblyException {
97
+ final long handle = use ();
98
+ final AblyRest rest = new AblyRest (clientOptions );
99
+ rest .setAndroidContext (applicationContext );
100
+ restInstances .put (handle , rest );
101
+ return handle ;
102
+ }
103
+
104
+ @ Override
105
+ public synchronized long createRealtime (final ClientOptions clientOptions , final Context applicationContext ) throws AblyException {
106
+ final long handle = use ();
107
+ final AblyRealtime realtime = new AblyRealtime (clientOptions );
108
+ realtime .setAndroidContext (applicationContext );
109
+ realtimeInstances .put (handle , realtime );
110
+ return handle ;
111
+ }
112
+
113
+ synchronized long use () {
114
+ if (used ) {
115
+ throw new IllegalStateException ("Reserved handle has already been used to create a client instance (handle=" + handle + ")." );
116
+ }
117
+ used = true ;
118
+ return handle ;
119
+ }
55
120
}
56
121
57
- AblyRest getRest ( final long handle ) {
58
- return restInstances . get ( handle );
122
+ synchronized ClientHandle reserveClientHandle ( ) {
123
+ return new ReservedClientHandle ( nextHandle . getAndIncrement () );
59
124
}
60
125
61
- long createRealtime (final ClientOptions clientOptions , Context applicationContext ) throws AblyException {
62
- final AblyRealtime realtime = new AblyRealtime (clientOptions );
63
- realtime .setAndroidContext (applicationContext );
64
- realtimeInstances .put (nextHandle , realtime );
65
- return nextHandle ++;
126
+ synchronized AblyRest getRest (final long handle ) {
127
+ return restInstances .get (handle );
66
128
}
67
129
68
- AblyRealtime getRealtime (final long handle ) {
130
+ synchronized AblyRealtime getRealtime (final long handle ) {
69
131
return realtimeInstances .get (handle );
70
132
}
71
133
@@ -79,38 +141,38 @@ AblyRealtime getRealtime(final long handle) {
79
141
* @param handle integer handle to either AblyRealtime or AblyRest
80
142
* @return AblyBase
81
143
*/
82
- AblyBase getAblyClient (final long handle ) {
144
+ synchronized AblyBase getAblyClient (final long handle ) {
83
145
AblyRealtime realtime = getRealtime (handle );
84
146
return (realtime != null ) ? realtime : getRest (handle );
85
147
}
86
148
87
- Push getPush (final long handle ) {
149
+ synchronized Push getPush (final long handle ) {
88
150
AblyRealtime realtime = getRealtime (handle );
89
151
return (realtime != null ) ? realtime .push : getRest (handle ).push ;
90
152
}
91
153
92
- PushChannel getPushChannel (final long handle , final String channelName ) {
154
+ synchronized PushChannel getPushChannel (final long handle , final String channelName ) {
93
155
return getAblyClient (handle )
94
156
.channels
95
157
.get (channelName ).push ;
96
158
}
97
159
98
- long setPaginatedResult (AsyncPaginatedResult result , Integer handle ) {
160
+ synchronized long setPaginatedResult (AsyncPaginatedResult result , Integer handle ) {
99
161
long longHandle ;
100
162
if (handle == null ) {
101
- longHandle = nextHandle ++ ;
163
+ longHandle = nextHandle . getAndIncrement () ;
102
164
} else {
103
165
longHandle = handle .longValue ();
104
166
}
105
167
paginatedResults .put (longHandle , result );
106
168
return longHandle ;
107
169
}
108
170
109
- AsyncPaginatedResult <Object > getPaginatedResult (long handle ) {
171
+ synchronized AsyncPaginatedResult <Object > getPaginatedResult (long handle ) {
110
172
return paginatedResults .get (handle );
111
173
}
112
174
113
- void reset () {
175
+ synchronized void reset () {
114
176
for (int i = 0 ; i < realtimeInstances .size (); i ++) {
115
177
long key = realtimeInstances .keyAt (i );
116
178
AblyRealtime r = realtimeInstances .get (key );
0 commit comments