溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android怎么使用GRPC進(jìn)行通信

發(fā)布時(shí)間:2023-02-28 13:39:28 來源:億速云 閱讀:104 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“Android怎么使用GRPC進(jìn)行通信”的相關(guān)知識(shí),小編通過實(shí)際案例向大家展示操作過程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“Android怎么使用GRPC進(jìn)行通信”文章能幫助大家解決問題。

    引言

    Android作為一個(gè)開發(fā)平臺(tái),本身是使用java進(jìn)行封裝的,因此java可以調(diào)用的庫(kù),在Android中同樣可以進(jìn)行調(diào)用,這樣就使得Android設(shè)備具有豐富的功能,可以進(jìn)行各種類型的開發(fā)。

    環(huán)境搭建

    工欲善其事,必先利其器。首先我們先來進(jìn)行開發(fā)環(huán)境的搭建。這里先要強(qiáng)調(diào)一下,Android開發(fā)中使用的項(xiàng)目管理工具Gradle對(duì)于版本的要求非常嚴(yán)格,如果不使用正確的版本號(hào),可能導(dǎo)致程序報(bào)錯(cuò),因此這一點(diǎn)需要特別注意。

    我們?cè)趧?chuàng)建完一個(gè)項(xiàng)目后,需要修改一些文件的信息,具體需要修改的文件信息如下

    Android怎么使用GRPC進(jìn)行通信

    對(duì)于上面的修改我們一個(gè)一個(gè)來看。

    修改項(xiàng)目的setting.gradle信息

    這個(gè)文件里面指定了gradle去哪個(gè)倉(cāng)庫(kù)中去找插件和第三方依賴庫(kù),我以及項(xiàng)目引入的模塊信息。

    我找到的一個(gè)可行的配置信息如下

    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'

    修改項(xiàng)目的build.gralde信息

    項(xiàng)目目錄下的build.gradle文件主要指定了項(xiàng)目中需要引入的插件,當(dāng)然在這個(gè)文件中主要是下載插件,我們需要到具體的模塊的build.gralde中去引入插件。

    在這個(gè)項(xiàng)目中,主要指定gradle插件和protobuf插件,我找到的一個(gè)可行配置如下

    // 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版本號(hào)

    這一步需要和你引入的gradle插件相關(guān)聯(lián),插件的版本和你引入的gradle版本必須要匹配才行,我引入的插件版本是7.2.0,引入的gralde版本是7.4。

    修改gradle版本一共有兩種方式,第一種就是在projectstructure中進(jìn)行修改。

    Android怎么使用GRPC進(jìn)行通信

    第二種方法就是直接在配置文件中進(jìn)行修改

    Android怎么使用GRPC進(jìn)行通信

    你需要哪個(gè)版本的gradle就直接在配置文件中指定對(duì)應(yīng)版本的壓縮包。

    這兩種修改方式都是等效的。

    修改模塊的build.gradle信息

    模塊的build.gradle中引入了插件,同時(shí)對(duì)插件做了一些配置,最最重要的就是引入第三方庫(kù)。

    我的配置信息如下

    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'
    }

    模塊編譯的時(shí)候會(huì)根據(jù)這個(gè)文件指定的信息進(jìn)行操作。這里最好根據(jù)你自己的配置文件,然后對(duì)比看看和上述文件有哪些缺失的信息,一般只需要添加缺失的信息即可,如果完全照搬上面的內(nèi)容可能導(dǎo)致項(xiàng)目報(bào)錯(cuò),因?yàn)槔锩嬗涗浟四惚旧淼捻?xiàng)目信息,可能和我的項(xiàng)目信息產(chǎn)生沖突。

    在main目錄下創(chuàng)建proto目錄

    我們需要?jiǎng)?chuàng)建一個(gè)和java目錄同級(jí)的proto文件夾,里面存放proto文件,這樣做是因?yàn)樵赽uild.gradle文件中指定了去proto文件夾中找到*.proto文件,并且編譯成java代碼。

    測(cè)試一下

    做完上述的幾個(gè)步驟后,我們可以編寫一個(gè)簡(jiǎn)單的grpc通信模型,測(cè)試一下環(huán)境是否搭建成功。

    首先在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;
    }

    然后編譯項(xiàng)目,我們可以在build目錄下看到對(duì)應(yīng)的java文件

    Android怎么使用GRPC進(jìn)行通信

    最后,我們可以使用一段簡(jiǎn)單的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文件中添加網(wǎng)絡(luò)權(quán)限

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
        <!-- 添加網(wǎng)絡(luò)權(quán)限 -->
        <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>

    最后編譯運(yùn)行,如果能看到控制臺(tái)中有如下信息表示環(huán)境搭建成功了,好耶ヾ(??▽?)ノ

    Android怎么使用GRPC進(jìn)行通信

    好了,到了這一步,我們可以將hello.proto和MainActivity中的代碼清除啦,這只是為了測(cè)試環(huán)境是否搭建成功而編寫的文件。

    GRPC的四種通信模式

    GRPC針對(duì)不同的業(yè)務(wù)場(chǎng)景,一共提供了四種通信模式,分別是簡(jiǎn)單一元模式,客戶端流模式,服務(wù)端流模式和雙向流模式,接下來這個(gè)進(jìn)行介紹。

    簡(jiǎn)單一元模式

    所謂簡(jiǎn)單一元模式,實(shí)際上就是客戶端和服務(wù)端進(jìn)行一問一答的通信。

    Android怎么使用GRPC進(jìn)行通信

    這種通信模式是最簡(jiǎn)單的,應(yīng)用場(chǎng)景有無線設(shè)備之間和客戶端之間保持連接的心跳檢測(cè),每隔一段時(shí)間就給服務(wù)端發(fā)送一個(gè)心跳檢測(cè)包,服務(wù)端接收到心跳包后就知道相應(yīng)客戶端處于連接狀態(tài)。

    在客戶端編寫如下程序

        // 簡(jiǎn)單一元模式
        public void simpleHello() {
            // 構(gòu)建簡(jiǎn)單的消息發(fā)送
            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 = "[服務(wù)端->客戶端]" + value.getRepInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "simpleHello onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "simpleHello onCompleted.");
                }
            });
        }

    服務(wù)端也需要編寫對(duì)應(yīng)的處理程序

        @Override
        public void simpleHello(Request request, StreamObserver<Reply> responseObserver) {
            Log.d(TAG, "服務(wù)端調(diào)用simpleHello.");
            String info = "[客戶端->服務(wù)端]" + request.getReqInfo();
            sendInfo(info);
            responseObserver.onNext(Reply.newBuilder().setRepInfo("simpleHello").build());
            responseObserver.onCompleted();
            super.simpleHello(request, responseObserver);
        }

    客戶端流模式

    客戶端流模式的意思就是客戶端可以一次性發(fā)送多個(gè)數(shù)據(jù)片段,當(dāng)然數(shù)據(jù)片段是一個(gè)類,具體的類有哪些字段都是你在最開始的proto文件中進(jìn)行指定的。這種模式的應(yīng)用場(chǎng)景就比如客戶端向服務(wù)端發(fā)送一連串的數(shù)據(jù),然后服務(wù)端最后發(fā)送一個(gè)響應(yīng)數(shù)據(jù)表示接收成功。

    Android怎么使用GRPC進(jìn)行通信

    在客戶端流模式中,客戶端可以在onCompleted之前使用多個(gè)onNext進(jìn)行數(shù)據(jù)發(fā)送。

    客戶端代碼如下

        // 客戶端流模式
        public void clientStream() {
            StreamObserver<Request> requestStreamObserver = stub.clientStream(new StreamObserver<Reply>() {
                @Override
                public void onNext(Reply value) {
                    Log.d(TAG, "clientStream onNext.");
                    String info = "[服務(wù)端->客戶端]" + 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();
        }

    服務(wù)端也需要編寫相應(yīng)代碼

        @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 = "[服務(wù)端->客戶端]" + value.getReqInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "clientStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "clientStream onCompleted.");
                    // 接收完所有消息后給客戶端發(fā)送消息
                    responseObserver.onNext(Reply.newBuilder().setRepInfo("clientStream").build());
                    responseObserver.onCompleted();
                }
            };
            return streamObserver;
        }

    服務(wù)端流模式

    服務(wù)端流模式和客戶端流模式正好相反,本質(zhì)都是差不多的,應(yīng)用場(chǎng)景有客戶端發(fā)送一個(gè)數(shù)據(jù)包告訴服務(wù)端,我需要某某數(shù)據(jù),然后服務(wù)器將對(duì)應(yīng)的所有信息都發(fā)送給客戶端。

    Android怎么使用GRPC進(jìn)行通信

    客戶端和服務(wù)端代碼分別如下所示

        // 服務(wù)端流模式
        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 = "[服務(wù)端->客戶端]" + 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 = "[客戶端->服務(wù)端]" + request.getReqInfo();
            sendInfo(info);
            responseObserver.onNext(Reply.newBuilder().setRepInfo("serverStream1").build());
            responseObserver.onNext(Reply.newBuilder().setRepInfo("serverStream2").build());
            responseObserver.onCompleted();
            super.serverStream(request, responseObserver);
        }

    雙向流模式

    雙向流模式是最后一種,也是最常用的一種,在這種模式中,客戶端和服務(wù)端的通信沒有什么限制,是比較理想的通信模式,應(yīng)用場(chǎng)景也最為廣泛,因?yàn)樵谶@種模式中,你也可以只發(fā)送一個(gè)數(shù)據(jù)包。

    Android怎么使用GRPC進(jìn)行通信

    客戶端和服務(wù)端的代碼如下

        // 雙向流模式
        public void bothFlowStream() {
            StreamObserver<Request> streamObserver = stub.bothFlowStream(new StreamObserver<Reply>() {
                @Override
                public void onNext(Reply value) {
                    Log.d(TAG, "bothFlowStream onNext.");
                    String info = "[服務(wù)端->客戶端]" + 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 = "[客戶端->服務(wù)端]" + 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;
        }

    簡(jiǎn)單的GRPC客戶端服務(wù)端程序設(shè)計(jì)

    上面介紹了GRPC的四種通信模式,以及各種模式中客戶端和服務(wù)端對(duì)應(yīng)的編寫方法。

    下面來介紹一下我們具體應(yīng)該如何編寫客戶端服務(wù)端代碼。

    我們一般會(huì)將客戶端和服務(wù)端分開來編寫,具體的文件如下圖所示

    Android怎么使用GRPC進(jìn)行通信

    首先需要編寫hello.proto文件,并且編譯后生成對(duì)應(yīng)的java文件,我們?cè)趐roto文件中編寫了兩個(gè)類用來請(qǐng)求和相應(yīng),并且編寫了四個(gè)接口方法,分別對(duì)應(yīng)GRPC請(qǐng)求響應(yīng)的四種模式

    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 {
        // 簡(jiǎn)單一元模式
        rpc simpleHello (Request) returns (Reply) {}
        // 客戶端流模式
        rpc clientStream (stream Request) returns (Reply) {}
        // 服務(wù)端流模式
        rpc serverStream (Request) returns (stream Reply) {}
        // 雙向流模式
        rpc bothFlowStream (stream Request) returns (stream Reply) {}
    }
    message Request {
        string reqInfo = 1;
    }
    message Reply {
        string repInfo = 1;
    }

    客戶端我們只需要編寫一個(gè)文件即可

    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;
        // 在構(gòu)造函數(shù)中連接
        public GRPCClient(Context context) {
            this.context = context;
            managedChannel = ManagedChannelBuilder.forAddress(host, port)
                    .usePlaintext()
                    .build();
            stub = GreeterGrpc.newStub(managedChannel);
        }
        // 使用廣播的方法發(fā)送數(shù)據(jù)更新ui
        private void sendInfo(String info) {
            Intent intent = new Intent("main.info");
            intent.putExtra("info", info);
            context.sendBroadcast(intent);
        }
        // 簡(jiǎn)單一元模式
        public void simpleHello() {
            // 構(gòu)建簡(jiǎn)單的消息發(fā)送
            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 = "[服務(wù)端->客戶端]" + 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 = "[服務(wù)端->客戶端]" + 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();
        }
        // 服務(wù)端流模式
        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 = "[服務(wù)端->客戶端]" + 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 = "[服務(wù)端->客戶端]" + 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();
        }
    }

    在構(gòu)造函數(shù)中,我們需要和服務(wù)端建立連接,所以一般需要服務(wù)端先啟動(dòng)。在連接建立完成后,無論調(diào)用什么方法都采用一個(gè)連接,然后分別編寫GRPC對(duì)應(yīng)的不同服務(wù)接口。

    服務(wù)端可以分成兩個(gè)類來編寫,其中GRPCServer主要用來啟動(dòng)服務(wù)端,GRPCServiceImpl則是繼承了GreeterGrpc.GreeterImplBase,可以重寫里面的方法,表示服務(wù)端如何處理GRPC請(qǐng)求。

    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, "服務(wù)端啟動(dòng)");
            sendInfo("服務(wù)端啟動(dòng)");
        }
        // 使用廣播的方法發(fā)送數(shù)據(jù)更新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;
        }
        // 使用廣播的方法發(fā)送數(shù)據(jù)更新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, "服務(wù)端調(diào)用simpleHello.");
            String info = "[客戶端->服務(wù)端]" + 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 = "[服務(wù)端->客戶端]" + value.getReqInfo();
                    sendInfo(info);
                }
                @Override
                public void onError(Throwable t) {
                    Log.d(TAG, "clientStream onError.");
                }
                @Override
                public void onCompleted() {
                    Log.d(TAG, "clientStream onCompleted.");
                    // 接收完所有消息后給客戶端發(fā)送消息
                    responseObserver.onNext(Reply.newBuilder().setRepInfo("clientStream").build());
                    responseObserver.onCompleted();
                }
            };
            return streamObserver;
        }
        @Override
        public void serverStream(Request request, StreamObserver<Reply> responseObserver) {
            String info = "[客戶端->服務(wù)端]" + 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 = "[客戶端->服務(wù)端]" + 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;
        }
    }

    我們采用一個(gè)簡(jiǎn)單的布局,就是四個(gè)按鈕,分別對(duì)應(yīng)GRPC的四個(gè)接口,然后在顯示客戶端和服務(wù)端發(fā)送給MainActivity的信息。這里面我們?cè)谛畔鬟f的時(shí)候采用了廣播的方法,為了能夠發(fā)送廣播,在實(shí)例化客戶端和服務(wù)端類的時(shí)候都需要傳遞Context作為參數(shù),這個(gè)Context就可以發(fā)送廣播了,然后在MainActivity中需要注冊(cè)一個(gè)廣播接收器,當(dāng)接收到具體信息的時(shí)候就更新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="服務(wù)端流模式" />
            <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;
        // 服務(wù)端和客戶端
        private GRPCClient grpcClient;
        private GRPCServer grpcServer;
        // 注冊(cè)一個(gè)廣播用于更新ui
        private BroadcastReceiver broadcastReceiver;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // 初始化控件
            initView();
            // 注冊(cè)廣播接收器
            register();
            // 初始化服務(wù)端和客戶端
            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());
        }
        // 注冊(cè)廣播更新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);
        }
    }

    最后在虛擬機(jī)上運(yùn)行程序,依次點(diǎn)擊四個(gè)按鈕,如果得到了下圖的結(jié)果,則表示程序跑通

    Android怎么使用GRPC進(jìn)行通信

    關(guān)于“Android怎么使用GRPC進(jìn)行通信”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI