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目录,并将官方的放到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代码可以点这里在我的中查看(也可以通过)
android方法数不能超过65535的问题
最后使用grpc,方法数会超过65535,可以使用com.android.support:multidex
去解决
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如有疑问大家可以留言交流,谢谢大家对的支持。