Android上使用grpc的方法教程
发布时间 - 2026-01-11 02:08:33 点击率:次前言

最近的一个项目使用到了grpc实现跨平台的远程调用,在安卓端使用的时候遇到了一些坑,这里记录一下。
首先根据grpc android的官方Demo配置grpc依赖,测试它的hello world工程。
编译谷歌官方的helloworld工程
添加rotobuf-gradle-plugin插件
首先添加rotobuf-gradle-plugin插件,他是用来从proto文件自动生成java代码的:
//Project的build.gradle中添加rotobuf-gradle-plugin插件
buildscript {
...
dependencies {
...
classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.0"
...
}
...
}
//App的build.gradle中添加下面配置
apply plugin: 'com.google.protobuf'
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.0.0'
}
plugins {
javalite {
artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0"
}
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.0.0' // CURRENT_GRPC_VERSION
}
}
generateProtoTasks {
all().each { task ->
task.plugins {
javalite {}
grpc {
// Options added to --grpc_out
option 'lite'
}
}
}
}
}
添加proto文件并自动生成java代码
在src/main/目录下创建一个proto目录,并将官方的helloworld.proto放到proto目录下
之后只需要rebuild一下就能看到build/generated/source/proto/目录下根据helloworld.proto生成了几个Java类
添加安卓端grpc的依赖
//App的build.gradle中添加下面配置
dependencies {
...
compile 'io.grpc:grpc-okhttp:1.1.2'
compile 'io.grpc:grpc-protobuf-lite:1.1.2'
compile 'io.grpc:grpc-stub:1.1.2'
compile 'javax.annotation:javax.annotation-api:1.2'
...
}
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.1'
}
我这个时候报了这个错误
Warning:Conflict with dependency ‘com.google.code.findbugs:jsr305'. Resolved versions for app (3.0.0) and test app (2.0.1) differ. See http://g.co/androidstudio/app-test-app-conflict for details.
这是因为com.google.code.findbugs:jsr305的版本不一致导致的
可以在App的build.gradle的android标签中配置一下解决
android {
...
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.1'
}
...
}
编写demo代码
public class MainActivity extends AppCompatActivity {
private static final String TAG = "GrpcDemo";
private static final int PROT = 55055;
private static final String NAME = "linjw";
private static final String HOST = "localhost";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startServer(PROT);
startClient(HOST, PROT, NAME);
}
private void startServer(int port){
try {
Server server = ServerBuilder.forPort(port)
.addService(new GreeterImpl())
.build()
.start();
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, e.getMessage());
}
}
private void startClient(String host, int port, String name){
new GrpcTask(host, port, name).execute();
}
private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
responseObserver.onNext(sayHello(request));
responseObserver.onCompleted();
}
private HelloReply sayHello(HelloRequest request) {
return HelloReply.newBuilder()
.setMessage("hello "+ request.getName())
.build();
}
}
private class GrpcTask extends AsyncTask<Void, Void, String> {
private String mHost;
private String mName;
private int mPort;
private ManagedChannel mChannel;
public GrpcTask(String host, int port, String name) {
this.mHost = host;
this.mName = name;
this.mPort = port;
}
@Override
protected void onPreExecute() {
}
@Override
protected String doInBackground(Void... nothing) {
try {
mChannel = ManagedChannelBuilder.forAddress(mHost, mPort)
.usePlaintext(true)
.build();
GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(mChannel);
HelloRequest message = HelloRequest.newBuilder().setName(mName).build();
HelloReply reply = stub.sayHello(message);
return reply.getMessage();
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.flush();
return "Failed... : " + System.lineSeparator() + sw;
}
}
@Override
protected void onPostExecute(String result) {
try {
mChannel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Log.d(TAG, result);
}
}
}
这段代码运行会崩溃:
Caused by: io.grpc.ManagedChannelProvider$ProviderNotFoundException: No functional server found. Try adding a dependency on the grpc-netty artifact
猜测google使用netty替代了okhttp,尝试换成grpc-netty的依赖:
dependencies {
...
compile 'io.grpc:grpc-netty:1.1.2'
compile 'io.grpc:grpc-protobuf-lite:1.1.2'
compile 'io.grpc:grpc-stub:1.1.2'
compile 'javax.annotation:javax.annotation-api:1.2'
...
}
这么编译会报错
com.android.build.api.transform.TransformException: com.android.builder.packaging.DuplicateFileException: Duplicate files copied in APK META-INF/INDEX.LIST
需要加上下面的配置解决
android {
...
packagingOptions {
pickFirst 'META-INF/INDEX.LIST'
pickFirst 'META-INF/LICENSE'
pickFirst 'META-INF/io.netty.versions.properties'
}
...
}
当然,还需要加上INTERNET权限,要不然运行的时候还是会崩溃。
最终就能看的下面的打印,这样安卓grpc的helloworld就成功了。
03-03 00:04:20.000 6137-6137/linjw.com.grpcdemo D/GrpcDemo: hello linjw
使用com.google.protobuf.Any
Any可以携带任意类型的数据,用法相当于c语言的void指针。在项目中是很常用的,但是谷歌在javalite的版本不支持Any。
如果在proto文件中使用了Any的话生成java代码就会有报错,例如将helloworld的proto文件改成下面的样子:
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
import "google/protobuf/any.proto";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (google.protobuf.Any) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
报错如下
google/protobuf/any.proto: File not found. helloworld.proto: Import “google/protobuf/any.proto” was not found or had errors. helloworld.proto:44:17: “google.protobuf.Any” is not defined.
使用grpc-jave代替grpc-javalite
但是现在做的这个项目的linux端实现已经用了Any,要改的话需要耗费比较大的精力。幸好尝试了下,发现安卓上也能跑支持Any的grpc-java。
首先我们要使用grpc-protobuf依赖替换grpc-protobuf-lite依赖
dependencies {
...
compile 'io.grpc:grpc-netty:1.1.2'
compile 'io.grpc:grpc-protobuf:1.1.2'
compile 'io.grpc:grpc-stub:1.1.2'
compile 'javax.annotation:javax.annotation-api:1.2'
...
}
接着修改protobuf-gradle-plugin配置使得自动生成java的代码而不是javalite的代码
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.0.0'
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.0.0' // CURRENT_GRPC_VERSION
}
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {}
}
task.plugins {
grpc {}
}
}
}
}
对应的修改helloworld的代码就能运行了
public class MainActivity extends AppCompatActivity {
private static final String TAG = "GrpcDemo";
private static final int PROT = 55055;
private static final String NAME = "linjw";
private static final String HOST = "localhost";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startServer(PROT);
startClient(HOST, PROT, NAME);
}
private void startServer(int port){
try {
Server server = ServerBuilder.forPort(port)
.addService(new GreeterImpl())
.build()
.start();
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG, e.getMessage());
}
}
private void startClient(String host, int port, String name){
new GrpcTask(host, port, name).execute();
}
private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
public void sayHello(Any request, StreamObserver<HelloReply> responseObserver) {
try {
responseObserver.onNext(sayHello(request.unpack(HelloRequest.class)));
responseObserver.onCompleted();
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
private HelloReply sayHello(HelloRequest request) {
return HelloReply.newBuilder()
.setMessage("hello "+ request.getName())
.build();
}
}
private class GrpcTask extends AsyncTask<Void, Void, String> {
private String mHost;
private String mName;
private int mPort;
private ManagedChannel mChannel;
public GrpcTask(String host, int port, String name) {
this.mHost = host;
this.mName = name;
this.mPort = port;
}
@Override
protected void onPreExecute() {
}
@Override
protected String doInBackground(Void... nothing) {
try {
mChannel = ManagedChannelBuilder.forAddress(mHost, mPort)
.usePlaintext(true)
.build();
GreeterGrpc.GreeterBlockingStub stub = GreeterGrpc.newBlockingStub(mChannel);
HelloRequest message = HelloRequest.newBuilder().setName(mName).build();
HelloReply reply = stub.sayHello(Any.pack(message));
return reply.getMessage();
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.flush();
return "Failed... : " + System.lineSeparator() + sw;
}
}
@Override
protected void onPostExecute(String result) {
try {
mChannel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Log.d(TAG, result);
}
}
}
完整的demo代码可以点这里在我的github中查看(也可以通过本地下载)
Android方法数不能超过65535的问题
最后使用grpc,方法数会超过65535,可以使用com.android.support:multidex去解决
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如有疑问大家可以留言交流,谢谢大家对的支持。
# android
# grpc
# 使用
# java
# Android使用GRPC进行通信过程解析
# Android搭建grpc环境过程分步详解
# 报错
# 自动生成
# 就能
# 目录下
# 几个
# 点这里
# 本地下载
# 他是
# 如有
# 也能
# 这段
# 可以通过
# 用了
# 并将
# 这个时候
# 还需要
# 不支持
# 只需要
# 可以使用
# 能看
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
焦点电影公司作品,电影焦点结局是什么?
动图在线制作网站有哪些,滑动动图图集怎么做?
Java解压缩zip - 解压缩多个文件或文件夹实例
在Oracle关闭情况下如何修改spfile的参数
智能起名网站制作软件有哪些,制作logo的软件?
EditPlus中的正则表达式实战(5)
Laravel如何实现多级无限分类_Laravel递归模型关联与树状数据输出【方法】
网站制作企业,网站的banner和导航栏是指什么?
使用Dockerfile构建java web环境
如何在Ubuntu系统下快速搭建WordPress个人网站?
Laravel如何使用Eloquent进行子查询
Laravel如何使用API Resources格式化JSON响应_Laravel数据资源封装与格式化输出
如何在Tomcat中配置并部署网站项目?
如何在万网利用已有域名快速建站?
Android 常见的图片加载框架详细介绍
如何在阿里云完成域名注册与建站?
Laravel如何实现多对多模型关联?(Eloquent教程)
香港服务器建站指南:免备案优势与SEO优化技巧全解析
Laravel如何集成第三方登录_Laravel Socialite实现微信QQ微博登录
如何确保FTP站点访问权限与数据传输安全?
零基础网站服务器架设实战:轻量应用与域名解析配置指南
Laravel怎么上传文件_Laravel图片上传及存储配置
EditPlus中的正则表达式实战(6)
如何快速生成专业多端适配建站电话?
公司网站制作价格怎么算,公司办个官网需要多少钱?
Android自定义listview布局实现上拉加载下拉刷新功能
如何在 Python 中将列表项按字母顺序编号(a.、b.、c. …)
5种Android数据存储方式汇总
免费制作统计图的网站有哪些,如何看待现如今年轻人买房难的情况?
jimdo怎样用html5做选项卡_jimdo选项卡html5实现与切换效果【指南】
如何快速生成可下载的建站源码工具?
百度浏览器ai对话怎么关 百度浏览器ai聊天窗口隐藏
Laravel项目怎么部署到Linux_Laravel Nginx配置详解
微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】
javascript中对象的定义、使用以及对象和原型链操作小结
Laravel的辅助函数有哪些_Laravel常用Helpers函数提高开发效率
如何制作公司的网站链接,公司想做一个网站,一般需要花多少钱?
Laravel如何使用Scope本地作用域_Laravel模型常用查询逻辑封装技巧【手册】
SQL查询语句优化的实用方法总结
利用 Google AI 进行 YouTube 视频 SEO 描述优化
韩国网站服务器搭建指南:VPS选购、域名解析与DNS配置推荐
如何选择PHP开源工具快速搭建网站?
C语言设计一个闪闪的圣诞树
济南网站建设制作公司,室内设计网站一般都有哪些功能?
图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?
如何快速搭建FTP站点实现文件共享?
如何选择可靠的免备案建站服务器?
敲碗10年!Mac系列传将迎来「触控与联网」双革新
安克发布新款氮化镓充电宝:体积缩小 30%,支持 200W 输出
详解一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)

