Skip to content

DataLayer library

DataStore documentation

Direct DataLayer sample code

DataLayer approach.

The Horologist DataLayer libraries, provide common abstractions on top of the Wearable DataLayer. These are built upon a common assumption of Google Protobuf and gRPC, which allows sharing data definitions throughout your Wear and Mobile apps.

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 {
        } catch (exception: InvalidProtocolBufferException) {
            throw CorruptionException("Cannot read proto.", exception)

    override suspend fun writeTo(t: CounterValue, output: OutputStream) {

val registry = WearDataLayerRegistry.fromContext(
    application = sampleApplication,
    coroutineScope = coroutineScope,
).apply {

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 Wearable DataClient with a single owner and multiple readers.

See DataStore.

Publishing a DataStore

private val dataStore: DataStore<CounterValue> by lazy {

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 {

        override suspend fun increment(request: GrpcDemoProto.CounterDelta): GrpcDemoProto.CounterValue {
            return dataStore.updateData {
                it.copy {
                    this.value = this.value +
                    this.updated = System.currentTimeMillis().toProtoTimestamp()

class WearCounterDataService : BaseGrpcDataService<CounterServiceGrpcKt.CounterServiceCoroutineImplBase>() {

    private val dataStore: DataStore<CounterValue> by lazy {

    override val registry: WearDataLayerRegistry by lazy {
            application = applicationContext,
            coroutineScope = lifecycleScope,
        ).apply {

    override fun buildService(): CounterServiceGrpcKt.CounterServiceCoroutineImplBase {
        return CounterService(dataStore)

Calling a remote service

val client = registry.grpcClient(
    nodeId = TargetNodeId.PairedPhone,
    coroutineScope = sampleApplication.servicesCoroutineScope,
) {

// Call the increment method from the proto service definition
val newValue: CounterValue =
    counterService.increment(counterDelta { delta = i.toLong() })


repositories {

dependencies {
    implementation "<version>"