A Beginner’s Guide to gRPC in Android
Last Updated 11/24/2019
What is gRPC?
gRPC stands for Google — Remote Procedure Call, and it is a framework for communication utilizing Protocol Buffers. While there is a lot of technical details as to what a Remote Procedure Call is, it allows a program on one machine (your Android phone) to call a subroutine on another machine (a server) without knowing that it is remote.
From w3.org
RPC is not a transport protocol: rather, it is a method of using existing communications features in a transparent way.
TLDR: gRPC allows your app and your backend to communicate in a simple, transparent, and efficient way.
Okay, but what are Protocol Buffers?
I’m glad you asked. Protocol Buffers, or protobufs as you may hear, is a method of serializing structured data. The great thing about protobufs is that they are language-neutral, meaning they can be used in many different programs, on many different platforms. Protocol Buffers are smaller, faster, and simpler while providing higher performance than other standards such as XML and JSON. You define your data schema, usually in a .proto file, and then use the protocol buffer compiler, protoc, to generate the source code, in this case, Java or Kotlin.
TLDR: Define your service and data schema in a .proto
file, compile it using protoc and use the generated source code to take advantage of Protocol Buffers.
Let’s get to some code!
Okay, let’s work through an example. Let’s say you have a .proto
file, for example, one like this:
This file defines a protocol for retrieving books from some backend. Let’s go through this file and explain what everything means.
syntax = “proto3”;
The first line of the file specifies that you’re using the syntax proto3
. If you don’t do this the protocol buffer compiler will assume you are using proto2
. This must be the first non-empty, non-comment line of the file.
package book;
In Java, the package
is used as the Java package, unless you explicitly provide the option java_package
in your .proto
file, which we do.
option java_package = "com.example.proto";
option java_outer_classname = "BookProto";
option java_package
is used to define the Java package where you want your generated Java class to be placed.
option java_outer_classname
is used to set the name of the generated Java class. If this is not set, protoc will use the name of the .proto
file.
message Book {
int64 isbn = 1;
string title = 2;
string author = 3;
}
This block of code defines the schema of your Book. This particular message is called a Book
, and has 3 fields:
int64 isbn
string title
string author
Without jumping too far ahead, when you make rpc
requests for a Book, it will return a Book
in this format.
message GetBookRequest {
int64 isbn = 1;
}
This block of code defines the schema of your request to get a book. This particular message is called a GetBookRequest
, and has 1 field, int64 isbn
.
service BookService {
rpc GetBook (GetBookRequest) returns (Book);
}
This last part of the .proto
file defines your service to request books, called BookService
. It has 1 function, named GetBook
, which takes a GetBookRequest
message as a parameter, and return a Book
message.
So what do I do with this file?
First, you’ll need to add this file to your Android Studio project. Create a directory named proto under /app/src/main/
and place this book.proto
file in there. Make sure to modify the package
, option java_package
, and option java_outer_classname
for your specific project.
Next, you’ll have to add the protobuf Gradle plugin to your project. In your project-level build.gradle
file add this import, classpath “com.google.protobuf:protobuf-gradle-plugin:0.8.10”
, as a dependency.
Moving to your module-level build.gradle
, add these following imports to the dependencies section:
implementation 'com.google.protobuf:protobuf-lite:3.0.1'
implementation 'io.grpc:grpc-okhttp:1.25.0' // CURRENT_GRPC_VERSION
implementation 'io.grpc:grpc-protobuf-lite:1.25.0' // CURRENT_GRPC_VERSION
implementation 'io.grpc:grpc-stub:1.25.0' // CURRENT_GRPC_VERSION
implementation 'javax.annotation:javax.annotation-api:1.3.2'
These imports are necessary for you to use gRPC and the generated classes in your project. Notice that we use the lite versions of these dependencies. It is recommended to use the lite versions in Android projects to save space. Sync your project!
Lastly, to get Android Studio to generate the Java classes from your book.proto
file, you’ll need to add some more code to your module-level build.gradle
.
At the top of the file, add as shown below:
apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf' // <-- Add this line!
apply plugin: 'kotlin-android'
Then add this code block:
protobuf {
protoc { artifact = 'com.google.protobuf:protoc:3.10.0' }
plugins {
javalite { artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0" }
grpc { artifact = 'io.grpc:protoc-gen-grpc-java:1.25.0' // CURRENT_GRPC_VERSION
}
}
generateProtoTasks {
all().each { task ->
task.plugins {
javalite {}
grpc { // Options added to --grpc_out
option 'lite' }
}
}
}
}
Without getting too deep, this block of code instructs Android Studio to use protoc to generate the required files from your book.proto
. The grpc
sections are not necessary if you aren’t using gRPC.
Your module-level build.gradle
should look like this if you’ve done everything right.
https://gist.github.com/vivekvinodh/c71d39d431bfc405fd59b721b5647756
Sync your project with your Gradle files and you should see your generated files in your project directory!
Okay, I have these files now but what can I do with them?
Alright, we’re nearing the end of this guide. Before we start writing code, there are two more concepts that you need to know about before you can start sending requests using gRPC, Channels and Stubs.
from grpc.io
A gRPC channel provides a connection to a gRPC server on a specified host and port and is used when creating a client stub (or just “client” in some languages).
from grpc.io
The client has a local object known as a stub (for some languages, the preferred term is client) that implements the same methods as the service. The client can then just call those methods on the local object, wrapping the parameters for the call in the appropriate protocol buffer message type — gRPC looks after sending the request(s) to the server and returning the server’s protocol buffer response(s).
TLDR: The channel is used to create a connection to the gRPC server and is needed to create a stub. The stub is what you use to call the methods that were defined in the proto file.
So how does this all end up looking!?
Here is a code block illustrating how to create a channel, create a stub from that channel, creating a book request, requesting a book from the server, and accessing the contents of the response.
// Kotlin
var managedChannel = ManagedChannelBuilder.forAddress("https://www.google.com", 443).build()
var blockingStub = BookServiceGrpc.newBlockingStub(managedChannel)
var bookRequest = BookProto.GetBookRequest.newBuilder().setIsbn(9123471293487).build()
var book = blockingStub.getBook(bookRequest)
book.author
book.isbn
book.title
And we’re done!!!
In this guide, we’ve gone over what gRPC and Protocol Buffers are, the structure of a Proto file, how to get Android Studio to generate Java classes from your Proto file, and how to use the generated classes to make requests to your gRPC server.
How you implement your networking and data storage logic is up to you but I’m personally a fan of the repository data pattern. Placing your networking services behind a repository ensures that your Activities/Fragments never have to concern themselves with the minutiae of your networking logic. This pairs well with MVVM.
Thanks for reading! I hope you learned something! This is my first guide on Medium so I appreciate any and all feedback if you got this far!😃