Search code examples
androidgrpc-javaandroid-binder

grpc-binder: how to send Parcelable from server to client


Parcelable data from client to server can use ParcelableUtils.metadataKey to build grpc [Metadata headers].

but, how to send Parcelable data from server to client??

[Metadata headers] is only client to server?


Solution

  • Metadata is available just before the request (i.e., headers), or just before or after the response (i.e., headers or trailers).

    The basic approach is the same in all the cases: use an interceptor. But how you use an interceptor and communicate with the interceptor varies between client and server.

    The header example shows a server interceptor adding metadata to response headers:

      @Override
      public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
          ServerCall<ReqT, RespT> call,
          final Metadata requestHeaders,
          ServerCallHandler<ReqT, RespT> next) {
        return next.startCall(new SimpleForwardingServerCall<ReqT, RespT>(call) {
          @Override
          public void sendHeaders(Metadata responseHeaders) {
            responseHeaders.put(CUSTOM_HEADER_KEY, "customRespondValue");
            super.sendHeaders(responseHeaders);
          }
        }, requestHeaders);
      }
    

    That approach is the same independent of the type of Metadata (text, binary, or Parcelable). The only difference for the different types of Metadata is the construction of the Metadata.Key.

    But with Binder, you are probably wanting the gRPC service to compute the Parcelable to respond with. For that, you need to pass a mutable API for the service to update.

      public static final Context.Key<AtomicReference<YourParcelable>> MY_KEY
          = Context.key("myparcelable"); // the string is just for debugging
    
      @Override
      public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
          ServerCall<ReqT, RespT> call,
          final Metadata requestHeaders,
          ServerCallHandler<ReqT, RespT> next) {
        // Context is immutable, so we put a mutable value inside it.
        final AtomicReference<YourParcelable> p = new AtomicReference<>();
        Context context = Context.current().withValue(MY_KEY, p);
        return Contexts.interceptCall(context, new SimpleForwardingServerCall<ReqT, RespT>(call) {
          @Override
          public void sendHeaders(Metadata responseHeaders) {
            YourParcelable yp = p.get();
            if (yp != null) {
              responseHeaders.put(CUSTOM_HEADER_KEY, yp);
            }
            super.sendHeaders(responseHeaders);
          }
        }, requestHeaders);
      }
    
    // In the service implementation before sending a response:
    MyInterceptor.MY_KEY.get().set(yourParcelable);