Horologist DataLayer library.¶
The Horologist DataLayer library, provide common abstractions on top of the Wearable DataLayer:
- Google Protobuf is used to structure the data.
- gRPC is used to define how messages are sent and received.
- DataStore is used to sync data across devices.
See this gradle build file for an example of configuring a build to use proto definitions.
syntax = "proto3";
message CounterValue {
int64 value = 1;
.google.protobuf.Timestamp updated = 2;
}
message CounterDelta {
int64 delta = 1;
}
service CounterService {
rpc getCounter(.google.protobuf.Empty) returns (CounterValue);
rpc increment(CounterDelta) returns (CounterValue);
}
Registering Serializers.¶
The WearDataLayerRegistry is an application singleton to register the Serializers.
object CounterValueSerializer : Serializer<CounterValue> {
override val defaultValue: CounterValue
get() = CounterValue.getDefaultInstance()
override suspend fun readFrom(input: InputStream): CounterValue =
try {
CounterValue.parseFrom(input)
} catch (exception: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", exception)
}
override suspend fun writeTo(t: CounterValue, output: OutputStream) {
t.writeTo(output)
}
}
val registry = WearDataLayerRegistry.fromContext(
application = sampleApplication,
coroutineScope = coroutineScope,
).apply {
registerSerializer(CounterValueSerializer)
}
Use Androidx DataStore¶
This library provides a new implementation of Androidx DataStore, in addition to the local Proto and Preferences implementations. The implementation uses the WearableDataClient with a single owner and multiple readers.
See DataStore.
Publishing a DataStore¶
private val dataStore: DataStore<CounterValue> by lazy {
registry.protoDataStore<CounterValue>(lifecycleScope)
}
Reading a remote DataStore¶
val counterFlow = registry.protoFlow<CounterValue>(TargetNodeId.PairedPhone)
Using gRPC¶
This library implements the gRPC transport over the Wearable MessageClient using the RPC request feature.
Implementing a service¶
class CounterService(val dataStore: DataStore<GrpcDemoProto.CounterValue>) :
CounterServiceGrpcKt.CounterServiceCoroutineImplBase() {
override suspend fun getCounter(request: Empty): GrpcDemoProto.CounterValue {
return dataStore.data.first()
}
override suspend fun increment(request: GrpcDemoProto.CounterDelta): GrpcDemoProto.CounterValue {
return dataStore.updateData {
it.copy {
this.value = this.value + request.delta
this.updated = System.currentTimeMillis().toProtoTimestamp()
}
}
}
}
class WearCounterDataService : BaseGrpcDataService<CounterServiceGrpcKt.CounterServiceCoroutineImplBase>() {
private val dataStore: DataStore<CounterValue> by lazy {
registry.protoDataStore<CounterValue>(lifecycleScope)
}
override val registry: WearDataLayerRegistry by lazy {
WearDataLayerRegistry.fromContext(
application = applicationContext,
coroutineScope = lifecycleScope,
).apply {
registerSerializer(CounterValueSerializer)
}
}
override fun buildService(): CounterServiceGrpcKt.CounterServiceCoroutineImplBase {
return CounterService(dataStore)
}
}
Calling a remote service¶
val client = registry.grpcClient(
nodeId = TargetNodeId.PairedPhone,
coroutineScope = sampleApplication.servicesCoroutineScope,
) {
CounterServiceGrpcKt.CounterServiceCoroutineStub(it)
}
// Call the increment method from the proto service definition
val newValue: CounterValue =
counterService.increment(counterDelta { delta = i.toLong() })
Download¶
repositories {
mavenCentral()
}
dependencies {
implementation "com.google.android.horologist:horologist-datalayer:<version>"
}