File: README.md
gRPC-Java - An RPC library and framework
| Homepage: | grpc.io |
|---|---|
| Mailing List: | [email protected] |
Supported Platforms
gRPC-Java supports Java 8 and later. Android minSdkVersion 24 (Nougat) and
later are supported with Java 8 language desugaring.
TLS usage on Android typically requires Play Services Dynamic Security Provider.
Please see the Security Readme.
Older Java versions are not directly supported, but a branch remains available
for fixes and releases. See gRFC P5 JDK Version Support
Policy.
| Java version | gRPC Branch |
|---|---|
| 7 | 1.41.x |
Getting Started
For a guided tour, take a look at the quick start
guide or the more explanatory gRPC
basics.
The examples and the
Android example
are standalone projects that showcase the usage of gRPC.
Download
Download the JARs. Or for Maven with non-Android, add to your pom.xml:
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.83.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.83.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.83.1</version>
</dependency>Or for Gradle with non-Android, add to your dependencies:
runtimeOnly 'io.grpc:grpc-netty-shaded:1.83.1'
implementation 'io.grpc:grpc-protobuf:1.83.1'
implementation 'io.grpc:grpc-stub:1.83.1'For Android client, use grpc-okhttp instead of grpc-netty-shaded andgrpc-protobuf-lite instead of grpc-protobuf:
implementation 'io.grpc:grpc-okhttp:1.83.1'
implementation 'io.grpc:grpc-protobuf-lite:1.83.1'
implementation 'io.grpc:grpc-stub:1.83.1'For Bazel, you can either
use Maven
(with the GAVs from above), or use @io_grpc_grpc_java//api et al (see below).
Development snapshots are available in Sonatypes's snapshot
repository.
Generated Code
For protobuf-based codegen, you can put your proto files in the src/main/proto
and src/test/proto directories along with an appropriate plugin.
For protobuf-based codegen integrated with the Maven build system, you can use
protobuf-maven-plugin (Eclipse and NetBeans users should also look atos-maven-plugin's
IDE documentation):
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.1</version>
</extension>
</extensions>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
com.google.protobuf:protoc:3.25.8:exe:${os.detected.classifier}
grpc-java
io.grpc:protoc-gen-grpc-java:1.83.1:exe:${os.detected.classifier}
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>For non-Android protobuf-based codegen integrated with the Gradle build system,
you can use protobuf-gradle-plugin:
plugins {
id 'com.google.protobuf' version '0.9.5'
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.25.8"
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.83.1'
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}The prebuilt protoc-gen-grpc-java binary uses glibc on Linux. If you are
compiling on Alpine Linux, you may want to use the Alpine grpc-java package
which uses musl instead.
For Android protobuf-based codegen integrated with the Gradle build system, also
use protobuf-gradle-plugin but specify the 'lite' options:
plugins {
id 'com.google.protobuf' version '0.9.5'
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.25.8"
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.83.1'
}
}
generateProtoTasks {
all().each { task ->
task.builtins {
java { option 'lite' }
}
task.plugins {
grpc { option 'lite' }
}
}
}
}For Bazel, use the proto_library
and the java_proto_library (no load() required)
and load("@io_grpc_grpc_java//:java_grpc_library.bzl", "java_grpc_library") (from this project), as in
this example BUILD.bazel.
API Stability
APIs annotated with @Internal are for internal use by the gRPC library and
should not be used by gRPC users. APIs annotated with @ExperimentalApi are
subject to change in future releases, and library code that other projects
may depend on should not use these APIs.
We recommend using the
grpc-java-api-checker
(an Error Prone plugin)
to check for usages of @ExperimentalApi and @Internal in any library code
that depends on gRPC. It may also be used to check for @Internal usage or
unintended @ExperimentalApi consumption in non-library code.
How to Build
If you are making changes to gRPC-Java, see the compiling
instructions.
High-level Components
At a high level there are three distinct layers to the library: Stub,
Channel, and Transport.
Stub
The Stub layer is what is exposed to most developers and provides type-safe
bindings to whatever datamodel/IDL/interface you are adapting. gRPC comes with
a plugin to the
protocol-buffers compiler that generates Stub interfaces out of .proto files,
but bindings to other datamodel/IDL are easy and encouraged.
Channel
The Channel layer is an abstraction over Transport handling that is suitable for
interception/decoration and exposes more behavior to the application than the
Stub layer. It is intended to be easy for application frameworks to use this
layer to address cross-cutting concerns such as logging, monitoring, auth, etc.
Transport
The Transport layer does the heavy lifting of putting and taking bytes off the
wire. The interfaces to it are abstract just enough to allow plugging in of
different implementations. Note the transport layer API is considered internal
to gRPC and has weaker API guarantees than the core API under package io.grpc.
gRPC comes with multiple Transport implementations:
- The Netty-based HTTP/2 transport is the main transport implementation based
on Netty. It is not officially supported on Android.
There is a "grpc-netty-shaded" version of this transport. It is generally
preferred over using the Netty-based transport directly as it requires less
dependency management and is easier to upgrade within many applications. - The OkHttp-based HTTP/2 transport is a lightweight transport based on
Okio and forked low-level parts of
OkHttp. It is mainly for use on Android. - The in-process transport is for when a server is in the same process as the
client. It is used frequently for testing, while also being safe for
production use. - The Binder transport is for Android cross-process communication on a single
device.
File: android-interop-testing/README.md
gRPC Android test App
Implements gRPC integration tests in an Android App.
In order to build this app, you need a local.properties file under this directory which specifies
the location of your android sdk:
sdk.dir=/somepath/somepath/sdkConnect your Android device or start the emulator:
$ ./start-emulator.sh <AVD name> & ./wait-for-emulator.shStart test server
Start the test server by:
$ ../run-test-server.shManually test
Install the App by:
$ ../gradlew installDebugThen manually test it with the UI.
Instrumentation tests
Instrumentation tests must be run on a connected device or emulator. Run with the
following gradle command:
$ ../gradlew connectedAndroidTest \
-Pandroid.testInstrumentationRunnerArguments.server_host=10.0.2.2 \
-Pandroid.testInstrumentationRunnerArguments.server_port=8080 \
-Pandroid.testInstrumentationRunnerArguments.use_tls=true \
-Pandroid.testInstrumentationRunnerArguments.server_host_override=foo.test.google.fr \
-Pandroid.testInstrumentationRunnerArguments.use_test_ca=true \
-Pandroid.testInstrumentationRunnerArguments.test_case=allFile: benchmarks/README.md
grpc Benchmarks
QPS Benchmark
The "Queries Per Second Benchmark" allows you to get a quick overview of the throughput and latency characteristics of grpc.
To build the benchmark type
$ ./gradlew :grpc-benchmarks:installDistfrom the grpc-java directory.
You can now find the client and the server executables in benchmarks/build/install/grpc-benchmarks/bin.
The C++ counterpart can be found at https://github.com/grpc/grpc/tree/master/test/cpp/qps
The netty benchmark directory contains
the standard benchmarks used to assess the performance of GRPC. Since these
benchmarks run on localhost over loopback the performance of the underlying network is considerably
different to real networks and their behavior. To address this issue we recommend the use of
a network emulator to make loopback behave more like a real network. To this end the benchmark
code looks for a loopback interface with 'benchmark' in its name and attempts to use the address
bound to that interface when creating the client and server. If it cannot find such an interface it
will print a warning and continue with the default localhost address.
To attempt to standardize benchmark behavior across machines we attempt to emulate a 10gbit
ethernet interface with a packet delay of 0.1ms.
Linux
On Linux we can use netem to shape the traffic appropriately.
# Remove all traffic shaping from loopback
sudo tc qdisc del dev lo root
# Add a priority traffic class to the root of loopback
sudo tc qdisc add dev lo root handle 1: prio
# Add a qdisc under the new class with the appropriate shaping
sudo tc qdisc add dev lo parent 1:1 handle 2: netem delay 0.1ms rate 10gbit
# Add a filter which selects the new qdisc class for traffic to 127.127.127.127
sudo tc filter add dev lo parent 1:0 protocol ip prio 1 u32 match ip dst 127.127.127.127 flowid 2:1
# Create an interface alias call 'lo:benchmark' that maps 127.127.127.127 to loopback
sudo ip addr add dev lo 127.127.127.127/32 label lo:benchmarkto remove this configuration
sudo tc qdisc del dev lo root
sudo ip addr del dev lo 127.127.127.127/32 label lo:benchmarkOther Platforms
Contributions are welcome!
Visualizing the Latency Distribution
The QPS client comes with the option --save_histogram=FILE, if set it serializes the histogram to FILE which can then be used with a plotter to visualize the latency distribution. The histogram is stored in the file format of HdrHistogram. That way it can be plotted very easily using a browser based tool like https://hdrhistogram.github.io/HdrHistogram/plotFiles.html. Simply upload the generated file and it will generate a beautiful graph for you. It also allows you to plot two or more histograms on the same surface in order two easily compare latency distributions.
JVM Options
When running a benchmark it's often useful to adjust some JVM options to improve performance and to gain some insights into what's happening. Passing JVM options to the QPS server and client is as easy as setting the JAVA_OPTS environment variables. Below are some options that I find very useful:
-Xmsgives a lower bound on the heap to allocate and-Xmxgives an upper bound. If your program uses more than what's specified in-Xmxthe JVM will exit with anOutOfMemoryError. When setting those always setXmsandXmxto the same value. The reason for this is that the young and old generation are sized according to the total available heap space. So if the total heap gets resized, they will also have to be resized and this will then trigger a full GC.-verbose:gcprints some basic information about garbage collection. It will log to stdout whenever a GC happend and will tell you about the kind of GC, pause time and memory compaction.-XX:+PrintGCDetailsprints out very detailed GC and heap usage information before the program terminates.-XX:-HeapDumpOnOutOfMemoryErrorand-XX:HeapDumpPath=pathwhen you are pushing the JVM hard it sometimes happens that it will crash due to the lack of available heap space. This option will allow you to dive into the details of why it happened. The heap dump can be viewed with e.g. the Eclipse Memory Analyzer.-XX:+PrintCompilationwill give you a detailed overview of what gets compiled, when it gets compiled, by which HotSpot compiler it gets compiled and such. It's a lot of output. I usually just redirect it to file and look at it withlessandgrep.-XX:+PrintInliningwill give you a detailed overview of what gets inlined and why some methods didn't get inlined. The output is very verbose and like-XX:+PrintCompilationand useful to look at after some major changes or when a drop in performance occurs.- It sometimes happens that a benchmark just doesn't make any progress, that is no bytes are transferred over the network, there is hardly any CPU utilization and low memory usage but the benchmark is still running. In that case it's useful to get a thread dump and see what's going on. HotSpot ships with a tool called
jpsandjstack.jpstells you the process id of all running JVMs on the machine, which you can then pass tojstackand it will print a thread dump of this JVM. - Taking a heap dump of a running JVM is similarly straightforward. First get the process id with
jpsand then usejmapto take the heap dump. You will almost always want to run it with-dump:livein order to only dump live objects. If possible, try to size the heap of your JVM (-Xmx) as small as possible in order to also keep the heap dump small. Large heap dumps are very painful and slow to analyze.
Profiling
Newer JVMs come with a built-in profiler called Java Flight Recorder. It's an excellent profiler and it can be used to start a recording directly on the command line, from within Java Mission Control or
with jcmd.
A good introduction on how it works and how to use it are http://hirt.se/blog/?p=364 and http://hirt.se/blog/?p=370.
File: compiler/README.md
gRPC Java Codegen Plugin for Protobuf Compiler
This generates the Java interfaces out of the service definition from a.proto file. It works with the Protobuf Compiler (protoc).
Normally you don't need to compile the codegen by yourself, since pre-compiled
binaries for common platforms are available on Maven Central:
- Navigate to https://mvnrepository.com/artifact/io.grpc/protoc-gen-grpc-java
- Click into a version
- Click "Files"
However, if the pre-compiled binaries are not compatible with your system,
you may want to build your own codegen.
Compiling and testing the codegen
Set up your system as described in COMPILING.md.
Then change to the compiler directory:
$ cd $GRPC_JAVA_ROOT/compilerTo compile the plugin:
$ ../gradlew java_pluginExecutableTo test the plugin with the compiler:
$ ../gradlew testYou will see a PASS if the test succeeds.
To compile a proto file and generate Java interfaces out of the service definitions:
$ protoc --plugin=protoc-gen-grpc-java=build/exe/java_plugin/protoc-gen-grpc-java \
--grpc-java_out="$OUTPUT_FILE" --proto_path="$DIR_OF_PROTO_FILE" "$PROTO_FILE"To generate Java interfaces with protobuf lite:
$ protoc --plugin=protoc-gen-grpc-java=build/exe/java_plugin/protoc-gen-grpc-java \
--grpc-java_out=lite:"$OUTPUT_FILE" --proto_path="$DIR_OF_PROTO_FILE" "$PROTO_FILE"Installing the codegen to Maven local repository
This will compile a codegen and put it under your ~/.m2/repository. This
will make it available to any build tool that pulls codegens from Maven
repositories.
$ ../gradlew publishToMavenLocalCreating a release of gRPC Java
Please follow the instructions in RELEASING.md under the root directory for
details on how to create a new release.
File: cronet/README.md
gRPC Cronet Transport
EXPERIMENTAL: gRPC's Cronet transport is an experimental API. Its stability
depends on upstream Cronet's implementation, which involves some experimental features.
This code enables using the Chromium networking stack
(Cronet)
as the transport layer for gRPC on Android. This lets your Android app make
RPCs using the same networking stack as used in the Chrome browser.
Some advantages of using Cronet with gRPC:
- Bundles an OpenSSL implementation, enabling TLS connections even on older
versions of Android without additional configuration - Robust to Android network connectivity changes
- Support for QUIC
Since gRPC's 1.24 release, the grpc-cronet package provides access to theCronetChannelBuilder class. Cronet jars are available on Google's Maven repository.
See the example app at https://github.com/GoogleChrome/cronet-sample/blob/master/README.md.
Example usage:
In your app module's build.gradle file, include a dependency on bothio.grpc:grpc-cronet and the Google Play Services Client Library for Cronet,com.google.android.gms:play-services-cronet.
In cases where Cronet cannot be loaded from Google Play services, there is a less performant
implementation of Cronet's API that can be used. Depend on org.chromium.net:cronet-fallback
to use this fall-back implementation.
You will also need permission to access the device's network state in yourAndroidManifest.xml:
Once the above steps are completed, you can create a gRPC Cronet channel as
follows:
import io.grpc.cronet.CronetChannelBuilder;
import org.chromium.net.ExperimentalCronetEngine;
...
ExperimentalCronetEngine engine =
new ExperimentalCronetEngine.Builder(context /* Android Context */).build();
ManagedChannel channel = CronetChannelBuilder.forAddress("localhost", 8080, engine).build();File: documentation/android-binderchannel-status-codes.md
Android gRPC/BinderChannel Status Codes
Background
BinderChannel is a gRPC transport that lets Android apps communicate across processes using familiar gRPC concepts and APIs. A BinderChannel-backed gRPC request can fail for many Android-specific reasons, both at ServiceConnection establishment and at transact() time. These transport-specific failures must be reported to clients using gRPCโs standard canonical status code abstraction. This document enumerates the BinderChannel errors one can expect to encounter, specifies a canonical status code mapping for each possibility and discusses how clients should handle them.
Status Code Mapping
Consider the table that follows as an BinderChannel-specific addendum to the โCodes that may be returned by the gRPC librariesโ document. Mappings in that table that share a status code with one of the binder-specific mappings are repeated here for comparison.
| # | Error Case | Android API Manifestation | Status Code | Expected Client Handling |
|---|---|---|---|---|
| 0 | Server app not visible. | bindService() returns false | UNIMPLEMENTEDโThe operation is not implemented or is not supported / enabled in this service.โ | Give up - This is an error in the client manifest. |
| 1 | Safer Intents violation. | Direct the user to install/reinstall the server app. | ||
| 2 | Server app not installed | |||
| 3 | Old version of the server app doesnโt declare the target android.app.Service in its manifest. | |||
| 4 | Target android.app.Service is disabled | |||
| 5 | The whole server app is disabled | |||
| 6 | Server app predates the Android M permissions model and the user must review and approve some newly requested permissions before it can run. | |||
| 7 | Target android.app.Service doesnโt recognize grpc binding Intent (old version of server app?) | onNullBinding() ServiceConnection callback | ||
| 8 | Method not found on the io.grpc.Server (old version of server app?) | N/A | ||
| 9 | Request cardinality violation (old version of server app expects unary rather than streaming, say) | |||
| 10 | Old version of the server app exposes target android.app.Service but doesnโt android:export it. | bindService() throws SecurityException | PERMISSION_DENIED | |
| โThe caller does not have permission to execute the specified operation โฆโ | Direct the user to update the server app in the hopes that a newer version fixes this error in its manifest. | |||
| 11 | Target android.app.Service requires an <android:permission> that client doesnโt hold. | Prompt the user to grant the needed Android permission | ||
| 12 | Violations of the security policy for miscellaneous Android features like android:isolatedProcess, android:externalService, android:singleUser, instant apps, BIND_TREAT_LIKE_ACTIVITY, etc, | Give up - This is a programming or packaging error that only the app developer can fix. | ||
| 13 | Calling Android UID not allowed by ServerSecurityPolicy | N/A | ||
| 14 | Server Android UID not allowed by clientโs SecurityPolicy | |||
| 15 | Server process crashed or killed with request in flight. | onDisconnected() ServiceConnection callback | UNAVAILABLE |
โThe service is currently unavailable. This is most likely a transient condition, which can be corrected by retrying with a backoff ...โ | Retry with exponential backoff and deadline (see ManagedChannelBuilder#enableRetry() |
| onBinderDied() IBinder.DeathRecipient callback | | | | |
| IBinder.transact() throws DeadObjectException | | | | |
| 16 | Server app is currently being upgraded to a new version | onBindingDied() ServiceConnection callback | | |
| 17 | The whole server app or the target android.app.Service was disabled | | | |
| 18 | Binder transaction buffer overflow | IBinder.transact() throws TransactionTooLargeException | | |
| 19 | Source Context for bindService() is destroyed with a request in flight | onDestroy() | CANCELLED
โThe operation was cancelled, typically by the caller.โ | Give up for now.
(Re. 19: The caller can try again later when the user opens the source Activity or restarts the source Service) |
| 20 | Client application cancelled the request | N/A | | |
| 21 | Bug in Android itself or the way the io.grpc.binder transport uses it. | IBinder.transact() returns false | INTERNAL
โThis means that some invariants expected by the underlying system have been broken. โฆ Reserved for serious errors.โ | Give up - This is a programming error that only the app or grpc developers can fix |
| bindService() throws IllegalArgumentException | | | | |
| 22 | Flow-control protocol violation | N/A | | |
| 23 | Canโt parse request/response proto | | | |
Ambiguity
We say a status code is ambiguous if it maps to two error cases that reasonable clients want to handle differently. For instance, a client may have good reasons to handle error cases 10 and 11 above differently. But they canโt do so based on status code alone because those error cases map to the same one.
In contrast, for example, even though error case 19 and 20 both map to the status code (CANCELLED), they are not ambiguous because we see no reason that clients would want to distinguish them. In both cases, clients will simply give up on the request.
Ambiguity of PERMISSION_DENIED and Mitigations
The mapping above has only one apparently ambiguous status code: PERMISSION_DENIED. However, this isnโt so bad because of the following:
The use of <android:permission>s for inter-app IPC access control (error case 11) is uncommon. Instead, we recommend that server apps only allow IPC from a limited set of client apps known in advance and identified by signature.
However, there may be gRPC server apps that want to use custom <android:permission>โs to let the end user decide which arbitrary other apps can make use of its gRPC services. In that case, clients should preempt error case 11 simply by checking whether they hold the required permissions before sending a request.
Server apps can avoid error case 10 by never reusing an android.app.Service as a gRPC host if it has ever been android:exported=false in some previous app version. Instead they should simply create a new android.app.Service for this purpose.
Only error cases 12 - 14 remain, making PERMISSION_DENIED unambiguous for the purpose of error handling. Reasonable client apps can handle it in a generic way by displaying an error message and/or proceeding with degraded functionality.
Non-Ambiguity of UNIMPLEMENTED
The UNIMPLEMENTED status code corresponds to quite a few different problems with the server app: Itโs either not installed, too old, misconfigured, or disabled in whole or in part. Despite the diversity of underlying error cases, we believe most client apps will and should handle UNIMPLEMENTED in the same way: by sending the user to the app store to (re)install the server app. Reinstalling might be overkill for the disabled cases but most end users don't know what it means to enable/disable an app and thereโs neither enough space in a UI dialog nor enough reader attention to explain it. Reinstalling is something users likely already understand and likely to cure problems 0-9 (once a fixed version of the server is available).
Detailed Discussion of Binder Failure Modes
IBinder.transact() returns false
According to the docs, false โgenerally means the transaction code was not understood.โ This is true for synchronous transactions but all gRPC/BinderChannel transactions are FLAG_ONEWAY meaning the calling thread doesnโt wait around for the server to return from onTransact(). Examination of the AOSP source code shows several additional undocumented reasons transact() could return false but all of these cases should be impossible and arenโt things that reasonable apps want to handle.
IBinder.transact() Throws an Exception
According to the docs, transact() can throw RemoteException but the significance of this exception isnโt documented. By inspection of the AOSP source, we see there are several cases:
- The remote process is no longer alive (
android.os.DeadObjectException) - The IPC buffer is full (
android.os.TransactionTooLargeException) - Certain internal errors from the underlying
ioctl()system call.
Status code mappings:
| Exception | Status Code | Rationale |
|---|---|---|
| android.os.DeadObjectException | UNAVAILABLE | So the caller can retry against a new incarnation of the server process |
| android.os.TransactionTooLargeException | UNAVAILABLE | These are usually transient. A retry is likely to succeed later when demand for the IPC buffer subsides. |
| Some other RemoteException | INTERNAL | So the caller doesnโt bother retrying |
| Some other RuntimeException | INTERNAL |
bindService() returns false
According to the docs, this bindService() returns false when โthe system couldn't find the service or if your client doesn't have permission to bind to it.โ However, the part about permission is somewhat misleading.
According to a review of the AOSP source code, there are in fact several cases:
- The target package is not installed
- The target package is installed but does not declare the target Service in its manifest.
- The target package requests dangerous permissions but targets sdk <= M and therefore requires a permissions review, but the caller is not running in the foreground and so it would be inappropriate to launch the review UI.
- The target package is not visible to the client due to Android 11 package visibility rules.
- One of the new Safer Intents rules is violated. Most commonly, the bind
Intentspecifies aComponentNameexplicitly but doesn't match any of its <intent-filter>s.
Status code mapping: UNIMPLEMENTED
(1) and (2) are interesting new possibilities unique to on-device RPC. (1) is straightforward and the most likely cause of (2) is that the user has an old version of the server app installed that predates its gRPC integration. Many clients will want to handle these cases, likely by directing the user to the app store in order to install/upgrade the server.
Unfortunately UNIMPLEMENTED doesnโt capture (3) but none of the other canonical status codes do either and we expect this case to be extremely rare.
(4) and (5) are intentially indistinguishable from (1) by Android design so we can't handle them differently. However, as an error in its own manifest, (4) isn't something a reasonable client would handle at runtime anyway. (5) is an error in the server manifest and so, just like the other cases, the best practice for handling it is to send the user to the app store in the hope that the server can be updated with a fix.
bindService() throws SecurityException
According to the docs, SecurityException is thrown โif the calling app does not have permission to bind to the given serviceโ. There are quite a few specific cases:
- The target Service sets
android:exported = โfalseโin its manifest but the caller is in a different app. - The target Service declares a required permission in its manifest but the calling package doesnโt have it.
- The target Service is marked as
android:singletonin the manifest but doesnโt hold theINTERACT_ACROSS_USERSpermission. - The caller is an
android:isolatedProcess. - The caller requested certain security-related flags on the binding without the necessary permission (gRPC/BinderChannel doesnโt do this)
- โฆ according to the source code, a long tail of unlikely security-related internal errors.
Status code mapping: PERMISSION_DENIED.
bindService() throws IllegalArgumentException
There are a couple cases:
- The binding Intent is not explicit.
- The binding Intent contains file descriptors.
Status Code mapping: INTERNAL. These cases should be impossible.
onBindingDied() ServiceConnection callback
According to the docs: โ... This means the interface will never receive another connection. The application will need to unbind and rebind the connection to activate it again. This may happen, for example, if the application hosting the service it is bound to has been updated.โ
Status code mapping: UNAVAILABLE
UNAVAILABLE is the best mapping since a retry is likely to succeed in the near future once the server application finishes updating.
onNullBinding() ServiceConnection callback
According to the docs: โCalled when the service being bound has returned null from its onBind() method. This indicates that the attempting service binding represented by this ServiceConnection will never become usable.โ
Status code mapping: UNIMPLEMENTED
UNIMPLEMENTED is used here because a retry is likely to fail for the same reason. The most likely root cause for a null binding is an older version of the server app where the android.app.Service exists but either doesnโt implement onBind() or doesnโt recognize the grpc.io.action.BIND Intent action.
onServiceDisconnected() ServiceConnection callback
According to the docs: โCalled when a connection to the Service has been lost. This typically happens when the process hosting the service has crashed or been killed ...โ
Status code mapping: UNAVAILABLE
UNAVAILABLE is used here since a retry is likely to succeed against a newly restarted instance of the server.
Response Parcel Contains an Exception
Androidโs Parcel class exposes a mechanism for marshalling certain types of RuntimeExceptions between traditional Binder IPC peers. However we wonโt consider this case because gRPC/BinderChannel doesnโt use this mechanism. In fact, all BinderChannel transactions are FLAG_ONE_WAY so there is no response Parcel.
Source Context for bindService() is Destroyed
The calling Activity or Service Context might be destroyed with a gRPC request in flight. Apps should cease operations when the Context hosting it goes away and this includes cancelling any outstanding RPCs.
Status code mapping: CANCELLED
File: documentation/android-channel-builder.md
AndroidChannelBuilder
The grpc-android package provides access to theAndroidChannelBuilder class. Given an Android Context, this builder
registers a network event listener upon channel construction. The listener
automatically responds to changes in the device's network state, avoiding
delays and interrupted RPCs that may otherwise occur.
By default, gRPC uses exponential backoff to recover from connection failures.
Depending on the scheduled backoff delay when the device regains connectivity,
this could result in a one minute or longer delay before gRPC re-establishes
the connection. This delay is removed when AndroidChannelBuilder is provided
with the app's Android Context. Notifications from the network listener
cause the channel to immediately reconnect upon network recovery.
On Android API levels 24+, AndroidChannelBuilder's network listener mechanism
allows graceful switching from cellular to wifi connections. When an Android
device on a cellular network connects to a wifi network, there is a brief
(typically 30 second) interval when both cellular and wifi networks remain
available, then any connections on the cellular network are terminated. By
listening for changes in the device's default network, AndroidChannelBuilder
sends new RPCs via wifi rather than using an already-established cellular
connection. Without listening for pending network changes, new RPCs sent on an
already established cellular connection would fail when the device terminates
cellular connections.
Note: Currently, AndroidChannelBuilder is only compatible with gRPC
OkHttp. We plan to offer additional Android-specific features compatible with
both the OkHttp and Cronet transports in the future, but the network listener
mechanism is only necessary with OkHttp; the Cronet library internally handles
connection management on Android devices.
Example usage:
In your build.gradle file, include a dependency on both io.grpc:grpc-android andio.grpc:grpc-okhttp.
You also need permission to access the device's network state in yourAndroidManifest.xml:
When constructing your channel, use AndroidChannelBuilder and provide it with
your app's Context:
import io.grpc.android.AndroidChannelBuilder;
...
ManagedChannel channel = AndroidChannelBuilder.forAddress("localhost", 8080)
.context(getApplicationContext())
.build();You continue to use the constructed channel exactly as you would any other
channel. gRPC will now monitor and respond to the device's network state
automatically. When you shutdown the managed channel, the network listener
registered by AndroidChannelBuilder will be unregistered.
File: documentation/server-reflection-tutorial.md
gRPC Server Reflection Tutorial
gRPC Server Reflection provides information about publicly-accessible gRPC
services on a server, and assists clients at runtime with constructing RPC
requests and responses without precompiled service information. It is used by
the gRPC command line tool (gRPC CLI), which can be used to introspect server
protos and send/receive test RPCs. Reflection is only supported for
proto-based services.
Enable Server Reflection
gRPC-Java Server Reflection is implemented byio.grpc.protobuf.services.ProtoReflectionServiceV1 in the grpc-services
package. To enable server reflection, you need to add theProtoReflectionServiceV1 to your gRPC server.
For example, to enable server reflection inexamples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java, we
need to make the following changes:
--- a/examples/build.gradle
+++ b/examples/build.gradle
@@ -27,6 +27,7 @@
dependencies {
compile "io.grpc:grpc-netty-shaded:${grpcVersion}"
compile "io.grpc:grpc-protobuf:${grpcVersion}"
+ compile "io.grpc:grpc-services:${grpcVersion}"
compile "io.grpc:grpc-stub:${grpcVersion}"
testCompile "junit:junit:4.13.2"
--- a/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java
+++ b/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java
@@ -33,6 +33,7 @@ package io.grpc.examples.helloworld;
import io.grpc.Server;
import io.grpc.ServerBuilder;
+import io.grpc.protobuf.services.ProtoReflectionServiceV1;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.logging.Logger;
@@ -50,6 +51,7 @@ public class HelloWorldServer {
int port = 50051;
server = ServerBuilder.forPort(port)
.addService(new GreeterImpl())
+ .addService(ProtoReflectionServiceV1.newInstance())
.build()
.start();
logger.info("Server started, listening on " + port);In the following examples, we assume you have made these changes to
enable reflection in HelloWorldServer.java.
gRPC CLI
After enabling server reflection in a server application, you can use gRPC
CLI to get information about its available services. gRPC CLI is written
in C++. Instructions on how to build gRPC CLI can be found at
command_line_tool.md.
Use gRPC CLI to check services
First, build and start the hello-world-server:
$ cd examples/
$ ./gradlew installDist
$ build/install/examples/bin/hello-world-serverOpen a new terminal and make sure you are in the directory where the grpc_cli
binary is located:
$ cd <grpc-cpp-directory>/bins/optList services
grpc_cli ls command lists services and methods exposed at a given port:
List all the services exposed at a given port
sh$ ./grpc_cli ls localhost:50051output:
shhelloworld.Greeter grpc.reflection.v1alpha.ServerReflectionList one service with details
grpc_cli lscommand inspects a service given its full name (in the format of
.<service>). It can print information with a long listing format
when-lflag is set. This flag can be used to get more details about a
service.sh$ ./grpc_cli ls localhost:50051 helloworld.Greeter -loutput:
shfilename: helloworld.proto package: helloworld; service Greeter { rpc SayHello(helloworld.HelloRequest) returns (helloworld.HelloReply) {} }
List methods
List one method with details
grpc_cli lscommand also inspects a method given its full name (in the
format of .<service>.<method>).sh$ ./grpc_cli ls localhost:50051 helloworld.Greeter.SayHello -loutput:
shrpc SayHello(helloworld.HelloRequest) returns (helloworld.HelloReply) {}
Inspect message types
We can usegrpc_cli type command to inspect request/response types given the
full name of the type (in the format of .<type>).
Get information about the request type
sh$ ./grpc_cli type localhost:50051 helloworld.HelloRequestoutput:
shmessage HelloRequest { optional string name = 1[json_name = "name"]; }
Call a remote method
We can send RPCs to a server and get responses using grpc_cli call command.
Call a unary method
sh$ ./grpc_cli call localhost:50051 SayHello "name: 'gRPC CLI'"output:
shmessage: "Hello gRPC CLI"
File: examples/android/strictmode/README.md
gRPC Android StrictMode Example
- This example intends to show the compatibility of gRPC with Android StrictMode.
- Android SDK version 28 is required for
StrictMode.VmPolicy.Builder.penaltyListenerused in the example. - This example does the same thing as HelloWorld example except popping up a dialog for detected StrictMode policy violation (shown below).
File: examples/android/README.md
gRPC Hello World Example (Android Java)
PREREQUISITES
Android Tutorial if you're new to Android development
We only have Android gRPC client in this example. Please follow examples in other languages to build and run a gRPC server.
INSTALL
Install gRPC Java library SNAPSHOT locally, including code generation plugin (Only need this step for non-released versions, e.g. master HEAD).
Install the app
$ cd helloworld # or "cd routeguide"
$ ../../gradlew installDebugPlease refer to the
tutorial on
how to use gRPC in Android programs.