Java client#
pw_rpc: Efficient, low-code-size RPC system for embedded devices
pw_rpc provides a Java / Kotlin client implementation under the package
dev.pigweed.pw_rpc for Android applications, desktop JVM tools, and
automated test frameworks.
Quick start#
The Java client interacts with an RPC server through Channels, Services, and MethodClients.
1. Create Channels and Client#
Define a channel with a send callback (e.g. over USB, BLE, or TCP sockets), and
instantiate the dev.pigweed.pw_rpc.Client:
public static Client createClient(Service sensorService) {
// 1. Define channel output
Channel channel = new Channel(1, (byte[] data) -> {
// Transmit byte buffer over physical transport (USB, Bluetooth, etc.)
System.out.println("Sending " + data.length + " bytes to device");
});
// 2. Instantiate Client
return Client.createMultiCall(List.of(channel), List.of(sensorService));
}
2. Route Incoming Packets#
When your transport receives incoming packets from the device, pass them into
client.processPacket():
public static void onDataReceived(Client client, byte[] rawPacket) {
client.processPacket(ByteBuffer.wrap(rawPacket));
}
3. Invoke RPC Methods#
Unary RPC#
Invoke a unary method using a dev.pigweed.pw_rpc.StreamObserver:
public static <TReq, TResp> Call callUnary(
Client client, MethodClient methodClient, TReq request) {
return methodClient.invokeUnary(request, new StreamObserver<TResp>() {
@Override
public void onNext(TResp response) {
System.out.println("Received response: " + response);
}
@Override
public void onCompleted(Status status) {
System.out.println("RPC finished with status: " + status);
}
@Override
public void onError(Status status) {
System.err.println("RPC failed with error: " + status);
}
});
}
Server Streaming RPC#
A server streaming RPC invokes the onNext callback for each streamed response
packet until the stream completes:
public static <TReq, TResp> Call callServerStreaming(
Client client, MethodClient methodClient, TReq request) {
return methodClient.invokeServerStreaming(request, new StreamObserver<TResp>() {
@Override
public void onNext(TResp response) {
System.out.println("Stream response: " + response);
}
@Override
public void onCompleted(Status status) {
System.out.println("Stream completed: " + status);
}
@Override
public void onError(Status status) {
System.err.println("Stream error: " + status);
}
});
}
Client & Bidirectional Streaming RPCs#
For client and bidirectional streaming calls, the returned call object allows streaming request messages:
public static <TReq, TResp> void callBidirectionalStreaming(
Client client, MethodClient methodClient, TReq chunk1, TReq chunk2) {
StreamObserver<TResp> observer = new StreamObserver<>() {
@Override
public void onNext(TResp response) {
System.out.println("Stream response: " + response);
}
@Override
public void onCompleted(Status status) {
System.out.println("Stream finished: " + status);
}
@Override
public void onError(Status status) {
System.err.println("Stream error: " + status);
}
};
Call.ClientStreaming<TReq> stream = methodClient.invokeBidirectionalStreaming(observer);
// Stream requests to server
stream.send(chunk1);
stream.send(chunk2);
// Finish sending from client
stream.finish();
}
Future-based invocation#
pw_rpc also supports ListenableFuture wrappers for asynchronous Java
code:
public static <TReq, TResp> void callUnaryFuture(
Client client, MethodClient methodClient, TReq request) {
ListenableFuture<UnaryResult<TResp>> future = methodClient.invokeUnaryFuture(request);
// Access result or attach listeners:
Futures.addCallback(future, new FutureCallback<UnaryResult<TResp>>() {
@Override
public void onSuccess(UnaryResult<TResp> result) {
if (result.status().ok()) {
System.out.println("Result: " + result.response());
}
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
}, MoreExecutors.directExecutor());
}