Android上使用grpc的方法教程

前言

最近的一个项目使用到了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,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,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,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,String name){
  new GrpcTask(host,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,String> {
  private String mHost;
  private String mName;
  private int mPort;
  private ManagedChannel mChannel;

  public GrpcTask(String host,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去解决

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如有疑问大家可以留言交流,谢谢大家对编程小技巧的支持。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


gRPC 前言 为什么使用gRPC 传输协议 传输效率 性能消耗 gRPC入门 gRPC流 证书认证 使用根证书 gRPC实现token认证 和Web服务共存 验证器 REST接口 grpcurl工具
参考文章: 1.&#160;https://www.cnblogs.com/kaixinyufeng/p/9651513.html 2.&#160;http://jia-shun.cn/2018/08
今天给大家翻译一篇由ASP.NET首席开发工程师 &quot;James Newton King&quot; 前几天发表的一篇博客,文中带来了一个实验性的产品gRPC Web。大家可以点击文末的讨论帖
上一篇文章我带着大家体验了一把《 &quot;ASP.NET Core 3.0 上的gRPC服务模板初体验(多图)&quot; 》,如果有兴趣的可以点击链接进行查看,相信跟着做的你,也是可以跑起来的。
早就听说ASP.NET Core 3.0中引入了gRPC的服务模板,正好趁着家里电脑刚做了新系统,然后装了VS2019的功夫来体验一把。同时记录体验的过程。如果你也想按照本文的步骤体验的话,那你得先安
这篇笔记主要是记录学习历程而不是怎么用~,以及protobuffers 和 gprc 各种文档的地址,等过上大半年后通过这篇笔记帮助自己快速重新掌握这个技术点 一、Protocolbuffers 关于
最近GRPC很火,感觉整RPC不用GRPC都快跟不上时髦了。 gRPC设计 gRPC是一种与语言无关的高性能远程过程调用 (RPC) 框架。刚好需要使用一个的RPC应用系统,自然而然就盯上了它,但是它
   gRPC是google开源提供的一个RPC软件框架,它的特点是极大简化了传统RPC的开发流程和代码量,使用户可以免除许多陷阱并聚焦于实际应用逻辑中。作为一种google的最新RPC解决方案,gRPC具备了以下这些强项: 1、gRPC在HTTP/2协议上用protobuf取代了json实现了最佳效率 2、用IDL(Interface Definition Language),一种简单的描述语言
  接着上期讨论的gRPC unary服务我们跟着介绍gRPC streaming,包括: Server-Streaming, Client-Streaming及Bidirectional-Streaming。我们首先在.proto文件里用IDL描述Server-Streaming服务: /* * responding stream of increment results */ servi
我已经设法通过GRPC使用流媒体模式的服务帐户为我的 Android应用程序运行Google Cloud Speech.但是,根据我所读到的内容,出于安全原因,我不应该在其中部署具有这些凭据的Android应用程序(当前存储为资源中的JSON文件).正确的是创建一个API密钥,如下所述: https://cloud.google.com/speech/docs/common/auth 这允许我限制
  安装protobuf go get -u github.com/golang/protobuf/proto go get -u github.com/golang/protobuf/protoc-gen-go 此时会生成protoc-gen-go,protoc一般是获取已经编译好的可执行文件(https://github.com/google/protobuf/releases)   安装gR
一、grpc安装 将 https://github.com/google/go-genproto 放到 $GOPATH/src/google.golang.org/genproto 将 https://github.com/grpc/grpc-go 放到 $GOPATH/src/google.golang.org/grpc 将 https://github.com/golang/t
参考URL: https://segmentfault.com/a/1190000015220713?utm_source=channel-hottest gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, P
我试图在电子应用程序中要求grpc,但我收到以下错误: Error: dlopen(/srv/node_modules/grpc/src/node/extension_binary/grpc_node.node, 1): Symbol not found: _GENERAL_NAME_free Referenced from: /srv/node_modules/grpc/src/node/e
我试图调用GRPC端点,但我想提供客户身份验证标头.我在哪里指定这个? var client = new proto.Publisher('127.0.0.1:50051', grpc.credentials.createInsecure()); var customHeader = { 'authorization': 'secret' } client.publish(d
我正在尝试创建一个 java grpc客户端来与go中的服务器通信.我是grpc的新手所以遵循本教程 gRPC Java Tutorial.在这些示例中,它们指的是阻塞和非阻塞存根,它们似乎是从 github的其他地方导入的. import io.grpc.examples.routeguide.RouteGuideGrpc.RouteGuideBlockingStub; import io.gr
我正在尝试做类似下面的事情(即使用流式grpc调用从客户端向服务器发送数据).代码参考取自官方网站上给出的grpc示例,用于解释目的: 客户端代码: ClientContext context; context.AddMetadata("authorization", "abcd"); context.set_deadline(...); std::unique_ptr<ClientWriter
什么是gRPC gRPC是google开源的一个高性能、跨语言的RPC框架,基于HTTP2协议,采用ProtoBuf 定义的IDL。 gRPC 的主要优点是: 现代高性能轻量级 RPC 框架。 协定优先 API 开发,默认使用协议缓冲区,允许与语言无关的实现。 可用于多种语言的工具,以生成强类型服务器和客户端。 支持客户端、服务器和双向流式处理调用。 使用 Protobuf 二进制序列化减少对网络
一.简介 gRPC 是一个由Google开源的,跨语言的,高性能的远程过程调用(RPC)框架。 gRPC使客户端和服务端应用程序可以透明地进行通信,并简化了连接系统的构建。它使用HTTP/2作为通信协议,使用 Protocol Buffers 作为序列化协议。 它的主要优点: 现代高性能轻量级 RPC 框架。 约定优先的 API 开发,默认使用 Protocol Buffers 作为描述语言,允许
目录 ASP.NET Core 3.0 使用gRPC ASP.NET Core 3.0 gRPC 双向流 ASP.NET Core 3.0 gRPC 认证授权 一.前言 在前一文 《ASP.NET Core 3.0 使用gRPC》中有提到 gRPC 支持双向流调用,支持实时推送消息,这也是 gRPC的一大特点,且 gRPC 在对双向流的控制支持上也是非常强大的。 二. 什么是 gRPC 流 gRP