問題在手機應用的開發中,通常會將複雜的業務邏輯層實現放在服務端,客戶端僅負責表現層。但是對於某些手機應用而言,業務邏輯的實現位於服務端反而是不安全的或是不合理的,而是需要將其邏輯直接在手機端實現。
目的
面對不同系統的手機客戶端,單獨重複實現相同的業務邏輯,並非最佳實踐。如何通過第三方語言 Go 語言將業務邏輯封裝成庫的形式,並以靜態打包的方式提供給不同系統的手機客戶端使用,是本次調研的目的。
理想目標圖:
具體調研內容包括:
- [x] iOS 應用實現 gRPC 調用
- [x] Android 應用實現 gRPC 調用
- [x] GoMobile SDK 在 iOS & Android 上的集成
- [x] GoMobile SDK 在 iOS & Android 上的邊界
- [ ] C/S 架構 or 靜態庫
其中關於 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 通信正常。完成該部分調研。