欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android上使用grpc的方法教程

程序员文章站 2022-06-04 21:46:10
前言 最近的一个项目使用到了grpc实现跨平台的远程调用,在安卓端使用的时候遇到了一些坑,这里记录一下。 首先根据grpc android的官方demo配置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目录,并将官方的放到proto目录下

之后只需要rebuild一下就能看到build/generated/source/proto/目录下根据helloworld.proto生成了几个java类

Android上使用grpc的方法教程

添加安卓端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代码可以点这里在我的中查看(也可以通过)

android方法数不能超过65535的问题

最后使用grpc,方法数会超过65535,可以使用com.android.support:multidex去解决

总结

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