Class SyncedStore
A small key/value store the platform carries between the devices one person signed in to, without them ever being in the same room.
This is the slow, patient half of continuity. com.codename1.continuity.Continuity hands the
current activity to a device that is here, now; this keeps a handful of durable settings --
which theme, which sort order, which tutorial they already dismissed, the id of the document
they are working through -- in step across everything they own.
SyncedStore.put("sortOrder", "byDate");
String order = SyncedStore.get("sortOrder", "byName");
What it is not
Not storage. Not a database, not a cache, and not a place for anything the app cannot cheerfully do without: the platform decides when to sync, the user can turn the whole mechanism off, and a device that has never been online has an empty store. Treat every read as "the value, or the default" -- which is why there is no read without a default.
Not secret. The contents leave the device and are held by the platform on the user's behalf.
Credentials belong in com.codename1.security.SecureStorage.
Not large. The platform imposes a total size and a key count, both small; put reports a
failure to write rather than pretending it stored something.
What it costs
Referencing this package is what makes an iOS build ask for the entitlement that gives the app
a synced store, which in turn requires the capability to be enabled on the App ID. That is why
it is a package of its own: an app that wants continuation to a nearby device and nothing else
should not have to arrange an entitlement to get it. Where the platform has no such store --
Android, desktop, the browser -- isSupported() is false and every call here is an inert
no-op, so the sensible shape is a synced value with a local default behind it.
Threading
Called on the event dispatch thread, like the rest of the toolkit. Codename One is single threaded by design -- one thread on each side of a native boundary, marshalled at the boundary rather than locked -- and this class follows that rule rather than making an exception to it.
It is worth stating because the simulation behind isSupported() == true on a desktop keeps
its key index as a second stored value: two threads writing different NEW keys at once would
each read that index, add their own key, and write it back, so one of them would vanish from
keys() while its value stayed readable by name. The platform stores have no such structure
and no such exposure. The answer is the toolkit's answer everywhere else -- call it from the
event thread, and use com.codename1.ui.Display#callSerially(Runnable) if you are on another
one -- not a lock inside a framework that does not have them.
-
Method Summary
Modifier and TypeMethodDescriptionstatic voidRegisters a listener for changes made on the user's other devices.static StringReads a value.static booleanWhether this platform has a store that follows the user between devices.static String[]keys()Every key currently in the store, in no particular order.static voidInternal.static booleanWrites a value, replacing any previous value for the key.static voidDeletes a key.static voidRemoves a listener.
-
Method Details
-
isSupported
public static boolean isSupported()Whether this platform has a store that follows the user between devices.
Returns
true when the store is available
-
put
Writes a value, replacing any previous value for the key.
Parameters
key: the key, must not be null or emptyvalue: the value, must not be null; useremove(String)to delete
Returns
true when the store holds the value afterwards; false when there is no store, or the platform would not take it -- a key count or a size past what it allows
Not gated on isSupported()
That was the THIRD layer this was wrong in.
isSupported() asks whether this build has a store that follows the user between devices, which is the right question for an application deciding whether to offer the feature and the wrong gate for the calls themselves. On iOS the store is a LOCAL persistent one whose cloud propagation is asynchronous, so reads and writes work and reach other devices later.
The gate was on all three of IOSNative.m, IOSContinuityBridge and here. Removing it from the first two changed nothing, because this one still made every call unreachable -- a fix verified at one layer and dead at the next. Each bridge answers for itself when there is no store: the Android one returns null and no-ops, the iOS one checks its own port flag, and the simulation reads local preferences.
-
get
Reads a value.
There is no overload without a default on purpose: the store is genuinely empty on a device that has not synced yet, so every read has to have an answer for that.
Parameters
key: the key, must not be null or emptydef: what to return when the key is absent or the store is unavailable
Returns
the value, or
def -
remove
Deletes a key. Deleting an absent key does nothing.
Parameters
key: the key, must not be null or empty
-
keys
Every key currently in the store, in no particular order.
Returns
the keys, never null and empty when the store is unavailable
-
addChangeListener
Registers a listener for changes made on the user's other devices.
Parameters
l: the listener
-
removeChangeListener
Removes a listener.
Parameters
l: the listener
-
notifyChanged
public static void notifyChanged()Internal. Invoked by the continuity framework when a port reports that the store changed underneath the app. Application code registers aSyncedStoreListenerinstead.
-