本篇文章使用的IDE是Android Studio。这里先吐槽一句,安卓项目搭建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 = "grpc_project_plus"
include ':app'
接着是项目的的build.gradle,这里面需要引入一些插件和进行gradle版本设置,其中gradle版本设置也是一个坑,版本号要设置对才行
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插件的版本是7.2.0,然后配置的gradle版本是7.4,具体的设置方法就是到gradle的配置文件中指定
当然也有一个简单的办法,就是到project structure中去指定
上面这张图按照我的理解,上面是插件的版本号,下面就是gradle的版本号,这两个不对应的话容易出问题,这里顺便说一下,我的Android Studio的版本应该是2021.3.1,就是海豚的图标。(经过这次的环境搭建,我现在对于版本号真的非常敏感,调版本号真的太折磨人了)
最后就是模块的build.gradle,我找到的一个能跑通demo的设置如下
plugins {
id 'com.android.application'
id 'com.google.protobuf'
}
android {
namespace 'com.example.grpc_project_plus'
compileSdk 32
defaultConfig {
applicationId "com.example.grpc_project_plus"
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 {
/*javalite {
artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0"
}*/
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 { // Options added to --grpc_out
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-okhttp:1.1.2'
implementation 'io.grpc:grpc-netty:1.1.2'
implementation 'io.grpc:grpc-protobuf-lite:1.1.2'
implementation 'io.grpc:grpc-stub:1.1.2'
implementation 'javax.annotation:javax.annotation-api:1.2'*/
// You need to build grpc-java to obtain these libraries below.
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'
}
编写proto文件并编译
配置好了上述环境后,我们需要编译proto文件。
首先在main目录下新建一个文件夹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;
}
这是一个简单的grpc接口调用,具体的语法我就不说了,这篇文章主要是讲环境搭建。
接着编译项目,如果编译成功的话,可以在build文件夹中看到grpc相关的java文件。
编写简单的demo代码
这是最后一步,在主Activity中编写简单的客户端和服务端代码测试grpc服务是否可以正常使用。
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.Grpc_project_plus"
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>
运行上述代码,如果能在控制台看到下面的这句话就说明环境搭建成功了。
结语
这次的环境搭建真的非常艰难,前前后后遇到了各种问题。不得不说,安卓编程对于版本太敏感了,只要有一个版本没有对应上项目都可能跑不通。
如果你遇到了什么问题欢迎在评论区交流,大家一起总结问题,少踩坑,共勉!