Android怎么使用GRPC进行通信

这篇文章主要介绍“Android怎么使用GRPC进行通信”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Android怎么使用GRPC进行通信”文章能帮助大家解决问题。

    引言

    Android作为一个开发平台,本身是使用java进行封装的,因此java可以调用的库,在Android中同样可以进行调用,这样就使得Android设备具有丰富的功能,可以进行各种类型的开发。

    环境搭建

    工欲善其事,必先利其器。首先我们先来进行开发环境的搭建。这里先要强调一下,Android开发中使用的项目管理工具Gradle对于版本的要求非常严格,如果不使用正确的版本号,可能导致程序报错,因此这一点需要特别注意。

    我们在创建完一个项目后,需要修改一些文件的信息,具体需要修改的文件信息如下

    Android怎么使用GRPC进行通信

    对于上面的修改我们一个一个来看。

    修改项目的setting.gradle信息

    这个文件里面指定了gradle去哪个仓库中去找插件和第三方依赖库,我以及项目引入的模块信息。

    我找到的一个可行的配置信息如下

    pluginManagement {
        repositories {
            gradlePluginPortal()
            google()
            mavenCentral()
        }
    }
    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            google()
            mavenCentral()
            maven { url 'https://jitpack.io' }
            maven { url 'https://repo.eclipse.org/content/repositories/paho-releases/'}
        }
    }
    rootProject.name = "gprc_learn"
    include ':app'

    修改项目的build.gralde信息

    项目目录下的build.gradle文件主要指定了项目中需要引入的插件,当然在这个文件中主要是下载插件,我们需要到具体的模块的build.gralde中去引入插件。

    在这个项目中,主要指定gradle插件和protobuf插件,我找到的一个可行配置如下

    // Top-level build file where you can add configuration options common to all sub-projects/modules.
    buildscript {
        repositories {
            maven{ url 'https://maven.aliyun.com/repository/jcenter'}
            maven { url 'https://maven.aliyun.com/repository/google' }
            maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
            maven { url 'https://maven.aliyun.com/repository/public' }
            google()
            mavenCentral()
        }
        dependencies {
            classpath "com.android.tools.build:gradle:7.2.0"
            classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.17"
        }
    }
    task clean(type: Delete) {
        delete rootProject.buildDir
    }

    修改gradle版本号

    这一步需要和你引入的gradle插件相关联,插件的版本和你引入的gradle版本必须要匹配才行,我引入的插件版本是7.2.0,引入的gralde版本是7.4。

    修改gradle版本一共有两种方式,第一种就是在projectstructure中进行修改。

    Android怎么使用GRPC进行通信

    第二种方法就是直接在配置文件中进行修改

    Android怎么使用GRPC进行通信

    你需要哪个版本的gradle就直接在配置文件中指定对应版本的压缩包。

    这两种修改方式都是等效的。

    修改模块的build.gradle信息

    模块的build.gradle中引入了插件,同时对插件做了一些配置,最最重要的就是引入第三方库。

    我的配置信息如下

    plugins {
        id 'com.android.application'
        id 'com.google.protobuf'
    }
    android {
        namespace 'com.example.grpc_learn'
        compileSdk 32
        defaultConfig {
            applicationId "com.example.grpc_learn"
            minSdk 29
            targetSdk 32
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
        configurations.all {
            resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.1'
            exclude group: 'com.google.guava', module: 'listenablefuture'
        }
        sourceSets {
            main {
                proto {
                    srcDir 'src/main/proto'
                }
            }
        }
        packagingOptions {
            pickFirst 'META-INF/INDEX.LIST'
            pickFirst 'META-INF/LICENSE'
            pickFirst 'META-INF/io.netty.versions.properties'
        }
    }
    protobuf {
        protoc {
            artifact = 'com.google.protobuf:protoc:3.17.2'
        }
        plugins {
            grpc {
                artifact = 'io.grpc:protoc-gen-grpc-java:1.39.0' // CURRENT_GRPC_VERSION
            }
        }
        generateProtoTasks {
            all().each { task ->
                task.builtins {
                    java { option 'lite' }
                }
                task.plugins {
                    grpc {
                        option 'lite' }
                }
            }
        }
    }
    dependencies {
        implementation 'androidx.appcompat:appcompat:1.4.1'
        implementation 'com.google.android.material:material:1.5.0'
        implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
        testImplementation 'junit:junit:4.13.2'
        androidTestImplementation 'androidx.test.ext:junit:1.1.3'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
        implementation 'io.grpc:grpc-netty:1.39.0'
        implementation 'io.grpc:grpc-okhttp:1.39.0' // CURRENT_GRPC_VERSION
        implementation 'io.grpc:grpc-protobuf-lite:1.39.0' // CURRENT_GRPC_VERSION
        implementation 'io.grpc:grpc-stub:1.39.0' // CURRENT_GRPC_VERSION
        implementation 'org.apache.tomcat:annotations-api:6.0.53'
    }

    模块编译的时候会根据这个文件指定的信息进行操作。这里最好根据你自己的配置文件,然后对比看看和上述文件有哪些缺失的信息,一般只需要添加缺失的信息即可,如果完全照搬上面的内容可能导致项目报错,因为里面记录了你本身的项目信息,可能和我的项目信息产生冲突。

    在main目录下创建proto目录

    我们需要创建一个和java目录同级的proto文件夹,里面存放proto文件,这样做是因为在build.gradle文件中指定了去proto文件夹中找到*.proto文件,并且编译成java代码。

    测试一下

    做完上述的几个步骤后,我们可以编写一个简单的grpc通信模型,测试一下环境是否搭建成功。

    首先在proto文件夹下编写hello.proto文件

    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;
    // The greeting service definition.
    service Greeter {
        // Sends a greeting
        rpc SayHello (HelloRequest) 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;
    }

    然后编译项目,我们可以在build目录下看到对应的java文件

    Android怎么使用GRPC进行通信

    最后,我们可以使用一段简单的grpc通信代码看看是否可以正常通信,我们直接修改MainActivity文件即可

    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "GrpcDemo";
        private static final int PROT = 56322;
        private static final String NAME = "hello world";
        private static final String HOST = "localhost";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Log.d(TAG, "start");
            startServer(PROT);
            Log.d(TAG, "start server.");
            startClient(HOST, PROT, NAME);
            Log.d(TAG, "start client.");
        }
        private void startServer(int port){
            try {
                NettyServerBuilder.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){
            ManagedChannel mChannel = ManagedChannelBuilder.forAddress(host, port)
                    .usePlaintext()
                    .build();
            GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(mChannel);
            HelloRequest message = HelloRequest.newBuilder().setName(name).build();
            stub.sayHello(message, new StreamObserver<HelloReply>() {
                @Override
                public void onNext(HelloReply value) {
                    //Log.d(TAG, "sayHello onNext.");
                    Log.d(TAG, value.getMessage());
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "sayHello onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "sayHello onCompleted.");
                }
            });
        }
        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(request.getName())
                        .build();
            }
        }
    }

    然后需要在AndroidManifest.xml文件中添加网络权限

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
        <!-- 添加网络权限 -->
        <uses-permission android:name="android.permission.INTERNET"/>
        <application
            android:allowBackup="true"
            android:dataExtractionRules="@xml/data_extraction_rules"
            android:fullBackupContent="@xml/backup_rules"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.Gprc_learn"
            tools:targetApi="31">
            <activity
                android:name=".MainActivity"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
                <meta-data
                    android:name="android.app.lib_name"
                    android:value="" />
            </activity>
        </application>
    </manifest>

    最后编译运行,如果能看到控制台中有如下信息表示环境搭建成功了,好耶ヾ(✿゚▽゚)ノ

    Android怎么使用GRPC进行通信

    好了,到了这一步,我们可以将hello.proto和MainActivity中的代码清除啦,这只是为了测试环境是否搭建成功而编写的文件。

    GRPC的四种通信模式

    GRPC针对不同的业务场景,一共提供了四种通信模式,分别是简单一元模式,客户端流模式,服务端流模式和双向流模式,接下来这个进行介绍。

    简单一元模式

    所谓简单一元模式,实际上就是客户端和服务端进行一问一答的通信。

    Android怎么使用GRPC进行通信

    这种通信模式是最简单的,应用场景有无线设备之间和客户端之间保持连接的心跳检测,每隔一段时间就给服务端发送一个心跳检测包,服务端接收到心跳包后就知道相应客户端处于连接状态。

    在客户端编写如下程序

        // 简单一元模式
        public void simpleHello() {
            // 构建简单的消息发送
            Request request = Request.newBuilder().setReqInfo("simpleHello").build();
            stub.simpleHello(request, new StreamObserver<Reply>() {
                @Override
                public void onNext(Reply value) {
                    Log.d(TAG, "simpleHello onNext.");
                    String info = "[服务端->客户端]" + value.getRepInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "simpleHello onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "simpleHello onCompleted.");
                }
            });
        }

    服务端也需要编写对应的处理程序

        @Override
        public void simpleHello(Request request, StreamObserver<Reply> responseObserver) {
            Log.d(TAG, "服务端调用simpleHello.");
            String info = "[客户端->服务端]" + request.getReqInfo();
            sendInfo(info);
            responseObserver.onNext(Reply.newBuilder().setRepInfo("simpleHello").build());
            responseObserver.onCompleted();
            super.simpleHello(request, responseObserver);
        }

    客户端流模式

    客户端流模式的意思就是客户端可以一次性发送多个数据片段,当然数据片段是一个类,具体的类有哪些字段都是你在最开始的proto文件中进行指定的。这种模式的应用场景就比如客户端向服务端发送一连串的数据,然后服务端最后发送一个响应数据表示接收成功。

    Android怎么使用GRPC进行通信

    在客户端流模式中,客户端可以在onCompleted之前使用多个onNext进行数据发送。

    客户端代码如下

        // 客户端流模式
        public void clientStream() {
            StreamObserver<Request> requestStreamObserver = stub.clientStream(new StreamObserver<Reply>() {
                @Override
                public void onNext(Reply value) {
                    Log.d(TAG, "clientStream onNext.");
                    String info = "[服务端->客户端]" + value.getRepInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "clientStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "clientStream onCompleted.");
                }
            });
            requestStreamObserver.onNext(Request.newBuilder().setReqInfo("clientStream1").build());
            requestStreamObserver.onNext(Request.newBuilder().setReqInfo("clientStream2").build());
            requestStreamObserver.onCompleted();
        }

    服务端也需要编写相应代码

        @Override
        public StreamObserver<Request> clientStream(StreamObserver<Reply> responseObserver) {
            StreamObserver<Request> streamObserver = new StreamObserver<Request>() {
                @Override
                public void onNext(Request value) {
                    Log.d(TAG, "clientStream onNext.");
                    String info = "[服务端->客户端]" + value.getReqInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "clientStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "clientStream onCompleted.");
                    // 接收完所有消息后给客户端发送消息
                    responseObserver.onNext(Reply.newBuilder().setRepInfo("clientStream").build());
                    responseObserver.onCompleted();
                }
            };
            return streamObserver;
        }

    服务端流模式

    服务端流模式和客户端流模式正好相反,本质都是差不多的,应用场景有客户端发送一个数据包告诉服务端,我需要某某数据,然后服务器将对应的所有信息都发送给客户端。

    Android怎么使用GRPC进行通信

    客户端和服务端代码分别如下所示

        // 服务端流模式
        public void serverStream() {
            Request request = Request.newBuilder().setReqInfo("serverStream").build();
            stub.serverStream(request, new StreamObserver<Reply>() {
                @Override
                public void onNext(Reply value) {
                    Log.d(TAG, "serverStream onNext.");
                    String info = "[服务端->客户端]" + value.getRepInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "serverStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "serverStream onCompleted.");
                }
            });
        }
        @Override
        public void serverStream(Request request, StreamObserver<Reply> responseObserver) {
            String info = "[客户端->服务端]" + request.getReqInfo();
            sendInfo(info);
            responseObserver.onNext(Reply.newBuilder().setRepInfo("serverStream1").build());
            responseObserver.onNext(Reply.newBuilder().setRepInfo("serverStream2").build());
            responseObserver.onCompleted();
            super.serverStream(request, responseObserver);
        }

    双向流模式

    双向流模式是最后一种,也是最常用的一种,在这种模式中,客户端和服务端的通信没有什么限制,是比较理想的通信模式,应用场景也最为广泛,因为在这种模式中,你也可以只发送一个数据包。

    Android怎么使用GRPC进行通信

    客户端和服务端的代码如下

        // 双向流模式
        public void bothFlowStream() {
            StreamObserver<Request> streamObserver = stub.bothFlowStream(new StreamObserver<Reply>() {
                @Override
                public void onNext(Reply value) {
                    Log.d(TAG, "bothFlowStream onNext.");
                    String info = "[服务端->客户端]" + value.getRepInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "bothFlowStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "bothFlowStream onCompleted.");
                }
            });
            streamObserver.onNext(Request.newBuilder().setReqInfo("bothFlowStream1").build());
            streamObserver.onNext(Request.newBuilder().setReqInfo("bothFlowStream2").build());
            streamObserver.onCompleted();
        }
        @Override
        public StreamObserver<Request> bothFlowStream(StreamObserver<Reply> responseObserver) {
            StreamObserver<Request> streamObserver = new StreamObserver<Request>() {
                @Override
                public void onNext(Request value) {
                    Log.d(TAG, "bothFlowStream onNext.");
                    String info = "[客户端->服务端]" + value.getReqInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "bothFlowStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "bothFlowStream onCompleted.");
                    responseObserver.onNext(Reply.newBuilder().setRepInfo("bothFlowStream1").build());
                    responseObserver.onNext(Reply.newBuilder().setRepInfo("bothFlowStream2").build());
                    responseObserver.onCompleted();
                }
            };
            return streamObserver;
        }

    简单的GRPC客户端服务端程序设计

    上面介绍了GRPC的四种通信模式,以及各种模式中客户端和服务端对应的编写方法。

    下面来介绍一下我们具体应该如何编写客户端服务端代码。

    我们一般会将客户端和服务端分开来编写,具体的文件如下图所示

    Android怎么使用GRPC进行通信

    首先需要编写hello.proto文件,并且编译后生成对应的java文件,我们在proto文件中编写了两个类用来请求和相应,并且编写了四个接口方法,分别对应GRPC请求响应的四种模式

    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;
    service Greeter {
        // 简单一元模式
        rpc simpleHello (Request) returns (Reply) {}
        // 客户端流模式
        rpc clientStream (stream Request) returns (Reply) {}
        // 服务端流模式
        rpc serverStream (Request) returns (stream Reply) {}
        // 双向流模式
        rpc bothFlowStream (stream Request) returns (stream Reply) {}
    }
    message Request {
        string reqInfo = 1;
    }
    message Reply {
        string repInfo = 1;
    }

    客户端我们只需要编写一个文件即可

    public class GRPCClient {
        private final String TAG = GRPCClient.class.toString();
        private final String host = "localhost";
        private final int port = 55056;
        private Context context;
        private ManagedChannel managedChannel;
        private GreeterGrpc.GreeterStub stub;
        // 在构造函数中连接
        public GRPCClient(Context context) {
            this.context = context;
            managedChannel = ManagedChannelBuilder.forAddress(host, port)
                    .usePlaintext()
                    .build();
            stub = GreeterGrpc.newStub(managedChannel);
        }
        // 使用广播的方法发送数据更新ui
        private void sendInfo(String info) {
            Intent intent = new Intent("main.info");
            intent.putExtra("info", info);
            context.sendBroadcast(intent);
        }
        // 简单一元模式
        public void simpleHello() {
            // 构建简单的消息发送
            Request request = Request.newBuilder().setReqInfo("simpleHello").build();
            stub.simpleHello(request, new StreamObserver<Reply>() {
                @Override
                public void onNext(Reply value) {
                    Log.d(TAG, "simpleHello onNext.");
                    String info = "[服务端->客户端]" + value.getRepInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "simpleHello onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "simpleHello onCompleted.");
                }
            });
        }
        // 客户端流模式
        public void clientStream() {
            StreamObserver<Request> requestStreamObserver = stub.clientStream(new StreamObserver<Reply>() {
                @Override
                public void onNext(Reply value) {
                    Log.d(TAG, "clientStream onNext.");
                    String info = "[服务端->客户端]" + value.getRepInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "clientStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "clientStream onCompleted.");
                }
            });
            requestStreamObserver.onNext(Request.newBuilder().setReqInfo("clientStream1").build());
            requestStreamObserver.onNext(Request.newBuilder().setReqInfo("clientStream2").build());
            requestStreamObserver.onCompleted();
        }
        // 服务端流模式
        public void serverStream() {
            Request request = Request.newBuilder().setReqInfo("serverStream").build();
            stub.serverStream(request, new StreamObserver<Reply>() {
                @Override
                public void onNext(Reply value) {
                    Log.d(TAG, "serverStream onNext.");
                    String info = "[服务端->客户端]" + value.getRepInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "serverStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "serverStream onCompleted.");
                }
            });
        }
        // 双向流模式
        public void bothFlowStream() {
            StreamObserver<Request> streamObserver = stub.bothFlowStream(new StreamObserver<Reply>() {
                @Override
                public void onNext(Reply value) {
                    Log.d(TAG, "bothFlowStream onNext.");
                    String info = "[服务端->客户端]" + value.getRepInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "bothFlowStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "bothFlowStream onCompleted.");
                }
            });
            streamObserver.onNext(Request.newBuilder().setReqInfo("bothFlowStream1").build());
            streamObserver.onNext(Request.newBuilder().setReqInfo("bothFlowStream2").build());
            streamObserver.onCompleted();
        }
    }

    在构造函数中,我们需要和服务端建立连接,所以一般需要服务端先启动。在连接建立完成后,无论调用什么方法都采用一个连接,然后分别编写GRPC对应的不同服务接口。

    服务端可以分成两个类来编写,其中GRPCServer主要用来启动服务端,GRPCServiceImpl则是继承了GreeterGrpc.GreeterImplBase,可以重写里面的方法,表示服务端如何处理GRPC请求。

    public class GRPCServer {
        private final String TAG = GRPCServer.class.toString();
        private final int port = 55056;
        private Context context;
        public GRPCServer(Context context) {
            this.context = context;
            start();
            Log.d(TAG, "服务端启动");
            sendInfo("服务端启动");
        }
        // 使用广播的方法发送数据更新ui
        private void sendInfo(String info) {
            Intent intent = new Intent("main.info");
            intent.putExtra("info", info);
            context.sendBroadcast(intent);
        }
        private void start() {
            try {
                NettyServerBuilder.forPort(port)
                        .addService(new GRPCServiceImpl(context))
                        .build()
                        .start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public class GRPCServiceImpl extends GreeterGrpc.GreeterImplBase {
        private final String TAG = GRPCServiceImpl.class.toString();
        private Context context;
        public GRPCServiceImpl(Context context) {
            this.context = context;
        }
        // 使用广播的方法发送数据更新ui
        private void sendInfo(String info) {
            Intent intent = new Intent("main.info");
            intent.putExtra("info", info);
            context.sendBroadcast(intent);
        }
        @Override
        public void simpleHello(Request request, StreamObserver<Reply> responseObserver) {
            Log.d(TAG, "服务端调用simpleHello.");
            String info = "[客户端->服务端]" + request.getReqInfo();
            sendInfo(info);
            responseObserver.onNext(Reply.newBuilder().setRepInfo("simpleHello").build());
            responseObserver.onCompleted();
            super.simpleHello(request, responseObserver);
        }
        @Override
        public StreamObserver<Request> clientStream(StreamObserver<Reply> responseObserver) {
            StreamObserver<Request> streamObserver = new StreamObserver<Request>() {
                @Override
                public void onNext(Request value) {
                    Log.d(TAG, "clientStream onNext.");
                    String info = "[服务端->客户端]" + value.getReqInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "clientStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "clientStream onCompleted.");
                    // 接收完所有消息后给客户端发送消息
                    responseObserver.onNext(Reply.newBuilder().setRepInfo("clientStream").build());
                    responseObserver.onCompleted();
                }
            };
            return streamObserver;
        }
        @Override
        public void serverStream(Request request, StreamObserver<Reply> responseObserver) {
            String info = "[客户端->服务端]" + request.getReqInfo();
            sendInfo(info);
            responseObserver.onNext(Reply.newBuilder().setRepInfo("serverStream1").build());
            responseObserver.onNext(Reply.newBuilder().setRepInfo("serverStream2").build());
            responseObserver.onCompleted();
            super.serverStream(request, responseObserver);
        }
        @Override
        public StreamObserver<Request> bothFlowStream(StreamObserver<Reply> responseObserver) {
            StreamObserver<Request> streamObserver = new StreamObserver<Request>() {
                @Override
                public void onNext(Request value) {
                    Log.d(TAG, "bothFlowStream onNext.");
                    String info = "[客户端->服务端]" + value.getReqInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "bothFlowStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "bothFlowStream onCompleted.");
                    responseObserver.onNext(Reply.newBuilder().setRepInfo("bothFlowStream1").build());
                    responseObserver.onNext(Reply.newBuilder().setRepInfo("bothFlowStream2").build());
                    responseObserver.onCompleted();
                }
            };
            return streamObserver;
        }
    }

    我们采用一个简单的布局,就是四个按钮,分别对应GRPC的四个接口,然后在显示客户端和服务端发送给MainActivity的信息。这里面我们在信息传递的时候采用了广播的方法,为了能够发送广播,在实例化客户端和服务端类的时候都需要传递Context作为参数,这个Context就可以发送广播了,然后在MainActivity中需要注册一个广播接收器,当接收到具体信息的时候就更新ui。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <Button
                android:id="@+id/button1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="一元模式" />
            <Button
                android:id="@+id/button2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="客户端流模式" />
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <Button
                android:id="@+id/button3"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="服务端流模式" />
            <Button
                android:id="@+id/button4"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="双向流模式" />
        </LinearLayout>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/text_title" />
        <TextView
            android:id="@+id/text_info"
            android:layout_width="match_parent"
            android:layout_height="418dp" />
    </LinearLayout>
    public class MainActivity extends AppCompatActivity {
        private final String TAG = MainActivity.class.toString();
        private Button button1;
        private Button button2;
        private Button button3;
        private Button button4;
        private TextView text_info;
        // 服务端和客户端
        private GRPCClient grpcClient;
        private GRPCServer grpcServer;
        // 注册一个广播用于更新ui
        private BroadcastReceiver broadcastReceiver;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // 初始化控件
            initView();
            // 注册广播接收器
            register();
            // 初始化服务端和客户端
            grpcClient = new GRPCClient(MainActivity.this);
            grpcServer = new GRPCServer(MainActivity.this);
        }
        // 初始化控件
        private void initView() {
            button1 = findViewById(R.id.button1);
            button1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    grpcClient.simpleHello();
                }
            });
            button2 = findViewById(R.id.button2);
            button2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    grpcClient.clientStream();
                }
            });
            button3 = findViewById(R.id.button3);
            button3.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    grpcClient.serverStream();
                }
            });
            button4 = findViewById(R.id.button4);
            button4.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    grpcClient.bothFlowStream();
                }
            });
            text_info = findViewById(R.id.text_info);
            text_info.setMovementMethod(new ScrollingMovementMethod());
        }
        // 注册广播更新ui
        private void register() {
            broadcastReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    Log.d(TAG, "广播收到消息" + intent.getStringExtra("info"));
                    text_info.append(intent.getStringExtra("info") + "\n");
                }
            };
            IntentFilter filter = new IntentFilter("main.info");
            registerReceiver(broadcastReceiver, filter);
        }
    }

    最后在虚拟机上运行程序,依次点击四个按钮,如果得到了下图的结果,则表示程序跑通

    Android怎么使用GRPC进行通信

    关于“Android怎么使用GRPC进行通信”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程之家行业资讯频道,小编每天都会为大家更新不同的知识点。

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

    相关推荐


    更新Android SDK到3.0版本时,遇到Failed to rename directory E:\android\tools to E:\android\temp\ToolPackage.old01问题,导致无法更新,出现该问题的原因是由于3.0版本与较早的sdk版本之间文件结构有冲突,解决
    Android 如何解决dialog弹出时无法捕捉Activity的back事件 在一些情况下,我们需要捕捉back键事件,然后在捕捉到的事件里写入我们需要进行的处理,通常可以采用下面三种办法捕捉到back事件: 1)重写onKeyDown或者onKeyUp方法 2)重写onBackPressed方
    Android实现自定义带文字和图片的Button 在Android开发中经常会需要用到带文字和图片的button,下面来讲解一下常用的实现办法。一.用系统自带的Button实现 最简单的一种办法就是利用系统自带的Button来实现,这种方式代码量最小。在Button的属性中有一个是drawable
    Android中的&quot;Unable to start activity ComponentInfo&quot;的错误 最近在做一款音乐播放器的时候,然后在调试的过程中发现一直报这个错误&quot;Unable to start activity ComponentInfo&quot;,从字面
    Android 关于长按back键退出应用程序的实现最近在做一个Android上的应用,碰到一个问题就是如何实现长按back键退出应用程序。在网上查找了很多资料,发现几乎没有这样的实现,大部分在处理时是双击back键来退出应用程序。参考了一下双击back键退出应用程序的代码,网上主流的一种方法是下面
    android自带的时间选择器只能精确到分,但是对于某些应用要求选择的时间精确到秒级,此时只有自定义去实现这样的时间选择器了。下面介绍一个可以精确到秒级的时间选择器。 先上效果图: 下面是工程目录: 这个控件我也是用的别人的,好像是一个老外写的,com.wheel中的WheelView是滑动控件的主
    Android平台下利用zxing实现二维码开发 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平台上应用比较成熟,而在Android平台上主流还是用zxing库,因此这里主要讲述如何利用zxing
    Android ListView的item背景色设置以及item点击无响应等相关问题 在Android开发中,listview控件是非常常用的控件,在大多数情况下,大家都会改掉listview的item默认的外观,下面讲解以下在使用listview时最常见的几个问题。1.如何改变item的背景色和按
    如何向Android模拟器中导入含有中文名称的文件在进行Android开发的时候,如果需要向Android模拟器中导入文件进行测试,通过DDMS下手动导入或者在命令行下通过adb push命令是无法导入含有中文文件名的文件的。后来发现借用其他工具可以向模拟器中导入中文名称的文件,这个工具就是Ultr
    Windows 下搭建Android开发环境一.下载并安装JDK版本要求JDK1.6+,下载JDK成功后进行安装,安装好后进行环境变量的配置【我的电脑】-——&gt;【属性】——&gt;【高级】 ——&gt;【环境变量】——&gt;【系统变量】中点击【新建】:变量名:CLASSPATH变量值:……
    如何利用PopupWindow实现弹出菜单并解决焦点获取以及与软键盘冲突问题 在android中有时候可能要实现一个底部弹出菜单,此时可以考虑用PopupWindow来实现。下面就来介绍一下如何使用PopupWindow实现一个弹出窗。 主Activity代码:public void onCreat
    解决Android中的ERROR: the user data image is used by another emulator. aborting的方法 今天调试代码的时候,突然出现这个错误,折腾了很久没有解决。最后在google上找到了大家给出的两种解决方案,下面给出这两种方法的链接博客:ht
    AdvserView.java package com.earen.viewflipper; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory;
    ImageView的scaleType的属性有好几种,分别是matrix(默认)、center、centerCrop、centerInside、fitCenter、fitEnd、fitStart、fitXY。 |值|说明| |:--:|:--| |center|保持原图的大小,显示在ImageVie
    文章浏览阅读8.8k次,点赞9次,收藏20次。本文操作环境:win10/Android studio 3.21.环境配置 在SDK Tools里选择 CMAKE/LLDB/NDK点击OK 安装这些插件. 2.创建CMakeLists.txt文件 在Project 目录下,右键app,点击新建File文件,命名为CMakeLists.txt点击OK,创建完毕! 3.配置文件 在CMa..._link c++ project with gradle
    文章浏览阅读1.2w次,点赞15次,收藏69次。实现目的:由mainActivity界面跳转到otherActivity界面1.写好两个layout文件,activity_main.xml和otherxml.xmlactivity_main.xml&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;RelativeLayout ="http://schemas..._android studio 界面跳转
    文章浏览阅读3.8w次。前言:最近在找Android上的全局代理软件来用,然后发现了这两款神作,都是外国的软件,而且都是开源的软件,因此把源码下载了下来,给有需要研究代理这方面的童鞋看看。不得不说,国外的开源精神十分浓,大家相互使用当前基础的开源软件,然后组合成一个更大更强的大开源软件。好吧,废话不多说,下面简单介绍一下这两款开源项目。一、ProxyDroid:ProxyDroid功能比较强大,用到的技术也比较多,源码也_proxydroid
    文章浏览阅读2.5w次,点赞17次,收藏6次。创建项目后,运行项目时Gradle Build 窗口却显示错误:程序包R不存在通常情况下是不会出现这个错误的。我是怎么遇到这个错误的呢?第一次创建项目,company Domain我使用的是:aven.com,但是创建过程在卡在了Building 'Calculator' Gradle Project info这个过程中,于是我选择了“Cancel”第二次创建项目,我还是使用相同的项目名称和项目路_r不存在
    文章浏览阅读8.9w次,点赞4次,收藏43次。前言:在Android上使用系统自带的代理,限制灰常大,仅支持系统自带的浏览器。这样像QQ、飞信、微博等这些单独的App都不能使用系统的代理。如何让所有软件都能正常代理呢?ProxyDroid这个软件能帮你解决!使用方法及步骤如下:一、推荐从Google Play下载ProxyDroid,目前最新版本是v2.6.6。二、对ProxyDroid进行配置(基本配置:) (1) Auto S_proxydroid使用教程
    文章浏览阅读1.1w次,点赞4次,收藏17次。Android Studio提供了一个很实用的工具Android设备监视器(Android device monitor),该监视器中最常用的一个工具就是DDMS(Dalvik Debug Monitor Service),是 Android 开发环境中的Dalvik虚拟机调试监控服务。可以进行的操作有:为测试设备截屏,查看特定进程中正在运行的线程以及堆栈信息、Logcat、广播状态信息、模拟电话_安卓摄像头调试工具