问题
在手机应用的开发中,通常会将复杂的业务逻辑层实现放在服务端,客户端仅负责表现层。但是对于某些手机应用而言,业务逻辑的实现位于服务端反而是不安全的或是不合理的,而是需要将其逻辑直接在手机端实现。
目的
面对不同系统的手机客户端,单独重复实现相同的业务逻辑,并非最佳实践。如何通过第三方语言 Go 语言将业务逻辑封装成库的形式,并以静态打包的方式提供给不同系统的手机客户端使用,是本次调研的目的。
理想目标图:
具体调研内容包括:
GoMobile SDK 在 iOS & Android 上的集成
GoMobile SDK 在 iOS & Android 上的边界
C/S 架构 or 静态库
其中关于 gRPC 在 iOS 与 Android 的实现,本身官方就已经提供了样例。本次调研会用到相关内容,所以将其作为调研的一部分记录下来,方便后来者阅读。调研中所有涉及的项目代码均存放于: liujianping/grpc-apps 仓库中, 需要的朋友可以直接下载测试。
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() {
@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 , 增加一行配置:
package="com.gitdig.androiddemo">
...
复制代码
构建程序,并在模拟器上执行,确认 gRPC 通信正常。完成该部分调研。