記一次技術調研(二): Android 應用實現 gRPC 調用

問題

在手機應用的開發中,通常會將複雜的業務邏輯層實現放在服務端,客戶端僅負責表現層。但是對於某些手機應用而言,業務邏輯的實現位於服務端反而是不安全的或是不合理的,而是需要將其邏輯直接在手機端實現。

目的

面對不同系統的手機客戶端,單獨重複實現相同的業務邏輯,並非最佳實踐。如何通過第三方語言 Go 語言將業務邏輯封裝成庫的形式,並以靜態打包的方式提供給不同系統的手機客戶端使用,是本次調研的目的。

理想目標圖:

具體調研內容包括:

其中關於 gRPC 在 iOS 與 Android 的實現,本身官方就已經提供了樣例。本次調研會用到相關內容,所以將其作爲調研的一部分記錄下來,方便後來者閱讀。調研中所有涉及的項目代碼均存放於: liujianping/grpc-apps 倉庫中, 需要的朋友可以直接下載測試。

更多最新文章請關注個人站點:GitDiG.com, 原文地址:Android 應用實現 gRPC 調用

1. 環境安裝

保證整個過程在"全球通"的環境下操作。

1.1 JDK 安裝

沒什麼好說的,下載安裝。設置好相應的環境變量。完成後,通過 java -version 命令確認安裝成功。

1.2 Android Studio 安裝

官網下載安裝包,按步驟安裝即可。

2. 開啓服務端

請參考iOS 應用實現 gRPC 調用服務端部分, 確認服務端服務啓動。

3. 創建 Android 項目

雖然項目grpc-java提供完整的樣例程序。但是缺少過程的結果,往往還是令初學者望而生畏。所以,從頭開始一步一步的記錄樣例的實現過程,是有必要的。

3.1 創建 Basic Activity 項目

創建 Basic Activity 初始項目,過程只是簡單的鼠標操作,同時輸入相應的項目名。這個過程就不多說了。完成創建後,打開 app/src/main/java/com/gitdig/androidDemo/MainActivity.java 文件, 添加一句日誌輸出:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
                // Demo 日誌輸出
                Log.i("demo", "Hello Android");

            }
        });
    }

構建程序,並在模擬器中執行。在模擬器的選擇中,可以選擇版本比較低的模擬器,會比較小,下載速度更快一點。啓動程序後,點擊按鈕,查看日誌輸出:I/demo: Hello Android. 初始程序構建完成。

3.2 新增 proto 文件,並生成 java 代碼

在 Android 項目中,通過 proto 文件生成 java 代碼的過程和 iOS 與 Go 的過程不一樣,沒有直接用到 protoc 命令行工具。而是通過 protobuf-gradle-plugin 插件的方式,在 Gradle 構建過程中自動生成。

首先配置插件以及 Gradle 構建過程, 將項目左欄視圖切換成 Android,在 Gradle 腳本欄中,首先修改 Project: androidDemo 項目級 build.gradle文件。增加 protobuf-gradle-plugin 插件支持。

buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.1'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        classpath 
        // 增加 protobuf-gradle-plugin 插件
        "com.google.protobuf:protobuf-gradle-plugin:0.8.8"
    }
}
...

在打開 Module: app 模塊級 build.gradle,增加相應的構建腳本:

apply plugin: 'com.android.application'
//應用插件
apply plugin: 'com.google.protobuf'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.0"
    defaultConfig {
        applicationId "com.gitdig.androiddemo"
        minSdkVersion 14
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

//插件構建腳本
protobuf {
    protoc {
        artifact = 'com.google.protobuf:protoc:3.0.0'
    }
    plugins {
        grpc {
            artifact = 'io.grpc:protoc-gen-grpc-java:1.0.0-pre2'
        }
        javalite {
            artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
        }
    }
    generateProtoTasks {
        all()*.plugins {
            javalite {}
        }
        ofNonTest()*.plugins {
            grpc {
                // Options added to --grpc_out
                option 'lite'
            }
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.google.android.material:material:1.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    // 增加插件依賴
    implementation 'javax.annotation:javax.annotation-api:1.2'
    implementation 'io.grpc:grpc-protobuf-lite:1.21.0'
    implementation 'io.grpc:grpc-okhttp:1.21.0'
    implementation 'io.grpc:grpc-stub:1.21.0'
}

以上 Gradle 腳本的配置就能實現構建過程中,通過 proto 文件自動生成 java 代碼了。 不過需要注意的就是,proto 文件的位置是固定的。再次將左側視圖切換到 Project 視圖。proto 文件的位置:app/src/main/proto 文件夾。

proto 文件夾與 proto 文件均通過手動創建。完成了以上過程,現在就可以構建一次項目,看看 java 文件是否生成。再切換會 Android 視圖,就會發現生成的代碼已經有了。

3.3 實現 gRPC 客戶端調用

實現 gRPC 客戶端的調用,代碼很簡單。首先是提供一個創建 gRPC 通信的客戶端連接。直接再 MainActivity 類中增加一個功能函數:

public class MainActivity extends AppCompatActivity {
    public static ManagedChannel newChannel(String host, int port) {
        return ManagedChannelBuilder.forAddress(host, port)
                .usePlaintext()
                .build();
    }
    ...
}

再在按鈕點擊事件中,發起客戶端 gRPC 請求:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        final GreeterGrpc.GreeterStub greeterStub = GreeterGrpc.newStub(newChannel("192.168.0.134", 50051));

        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
                Log.i("demo", "Hello Android");

                //增加 gRPC 請求,打印日誌
                HelloRequest request = HelloRequest.newBuilder().setName("JayL").build();
                greeterStub.sayHello(request, new StreamObserver<HelloReply>() {
                    @Override
                    public void onNext(HelloReply value) {
                        Log.i("demo", value.getMessage());
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.e("demo", t.getMessage());
                    }

                    @Override
                    public void onCompleted() {

                    }
                });

            }
        });
    }

因爲涉及網絡通信,打開模塊app的配置文件: app/src/main/AndroidManifest.xml, 增加一行配置:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.gitdig.androiddemo">
    <!-- 開啓網絡訪問權限 -->
    <uses-permission android:name="android.permission.INTERNET"/>
...
<manifest>

構建程序,並在模擬器上執行,確認 gRPC 通信正常。完成該部分調研。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章