一、原因:android项目中要用到底层底层给定的protobuf接口,但如何解析或者说如何使用不懂,故咨询一波。
二、已查到的相关资料:
1、定义:Protocol Buffers(也称protobuf)是google旗下一款独立于开发语言,独立于平台的可扩展的结构化数据序列机制。简单来说,就是像json、xml这种用来传输数据的一种数据交互协议。不过,相比与json、xml,protobuf更加轻便与高效。
更多详细资料可查看官网:
2、入门级别博客:
发现的几篇后面应该去跟着尝试做一下的博客:
http://www.gaohaiyan.com/2134.html
https://www.jianshu.com/p/df200894f5da
https://gqdy365.iteye.com/blog/2163076
3、我按照访问量最高,看了很多博客差不多的进行操作,决定据此进行配置,https://www.jianshu.com/p/fcf7f8cc0d8d
试着配置完,发现如下问题,多次修改设置,编译仍然无法通过,该bug仍然无法消退
org.gradle.api.tasks.TaskValidationException: A problem was found with the configuration of task ':app:generateDebugProto'.
at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.report(ValidatingTaskExecuter.java:66)
at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:53)
at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:119)
at org.gradle.api.internal.tasks.execution.ResolvePreviousStateExecuter.execute(ResolvePreviousStateExecuter.java:43)
at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:93)
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:45)
at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:94)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:56)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:55)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:67)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:49)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:315)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:305)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:101)
at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:49)
at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:43)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:322)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:134)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:129)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:202)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:193)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:129)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.gradle.api.InvalidUserDataException: Directory 'C:\Users\zhouxueli\AndroidStudioProjects\Protobuf\app\build\extracted-include-protos\main' specified for property '$3' does not exist.
at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.report(ValidatingTaskExecuter.java:63)
... 34 more
在build下 的如下目录extracted-include-protos 一直无法生成main文件夹,只有debug文件夹
昨天看了n篇文章之后,发现问题出在配置文件中,因为我初次时参考的博客链接如下
https://www.jianshu.com/p/0da79242babc
报错一直提示$3有问题,
//将会在 "$projectDir/src/generated"这个目录中自动生成对应的java文件
generatedFilesBaseDir = "$projectDir/src/generated"
同时能看到:
WARNING: Folder C:\Users\zhouxueli\AndroidStudioProjects\Protobuf\app\src\generated\debugUnitTest\java
INFO: 3rd-party Gradle plug-ins may be the cause
当昨晚再次打开前几天搁置下来的这个问题时,再次对照配置文件,想起这一项,当我把该项删除,编译便通过了
对于如上配置项,可能是博主自己系统中在哪里配置了什么东西。
若有类似参考如上文档的小伙伴,可参考本实践尝试修改解决。
最终我的简单测试步骤如下:
1、在android studio 中安装protobuf support 插件:File——>Settings——>Plugins——搜索protobuf support 安装即可;
2、创建一个普通的android项目,在负责本项目整个配置的build.gradle 文件中进行配置:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
//该项配置和gradle 要兼容,貌似gradle 3以下,要配置0.8.2以下,具体后续补充
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.10'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
3、在android项目的app目录下的build.gradle目录中进行配置
apply plugin: 'com.android.application'
//注意这里
apply plugin: 'com.google.protobuf'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.example.zhouxueli.protobuf"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
//如下和该测试紧密相关
sourceSets {
main {
proto {
//main目录新建proto目录
srcDir 'src/main/protobuf'
include '**/*.proto'
}
java {
srcDir 'src/main/java'
}
}
}
}
//构建task 和protobuf紧密相关
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.1.0'
}
generateProtoTasks {
all().each { task ->
task.builtins {
remove java
}
task.builtins {
java {}
// Add cpp output without any option.
// DO NOT omit the braces if you want this builtin to be added.
// cpp {} //java 项目的话,该项可删除
}
}
}
//如下为参考博客进行配置时踩到的一个小坑,源于自己对配置项含义不理解
//generatedFilesBaseDir = "$projectDir/src/generated"
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
//这个才是和protobuf配置相关的 start ~
implementation 'com.google.protobuf:protobuf-java:3.1.0'
implementation 'com.google.protobuf:protoc:3.1.0'
//这个才是和protobuf配置相关的 end ~
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
完成到此,android studio中的配置环境的问题就完成了,下面开始演示后续实例
4、在app ——> src——》main目录下,和java同层次的目录,创建一个protobuf 文件夹,该文件夹的命名和上方配置有关
//如下和该测试紧密相关
sourceSets {
main {
proto {
//main目录新建proto目录
srcDir 'src/main/protobuf'
include '**/*.proto'
}
java {
srcDir 'src/main/java'
}
}
}
这里的srcDir目录即为刚才为何要创建如此目录的原因。注protobuf的命名并非非要用protobuf,只要这里两个地方配置一致即可
然后即可在protobuf 目录中创建自己的实例 .proto文件
如我当前创建了一个Adrressbook.proto文件
package tutorial;
option java_package = "com.example.zhouxueli.test";
option java_outer_classname = "AddressBookProto";
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
message AddressBook {
repeated Person person = 1;
}
简单来说,如上是一个类似通讯簿的简单实例,其中可以存储多个人信息,每个人有自己的姓名 邮箱 person中的id貌似是这个结构必须有的。然后事实上,一个人可能拥有多个电话,所以结构如上,你也可以做一个简单的,只有单层的而非嵌套的message出来。
5、我在activity调用的地方,代码如下:
package com.example.zhouxueli.protobuf;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.example.zhouxueli.test.AddressBookProto;
import com.google.protobuf.InvalidProtocolBufferException;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.ceshitext);
AddressBookProto.Person person = AddressBookProto.Person.newBuilder()
.setId(1)
.setName("测试用文言")
.setEmail("123")
.build();
AddressBookProto.AddressBook book = AddressBookProto.AddressBook.newBuilder()
.addPerson(person)
.build();
//如下是为了演示我们工程项目中实际使用时的方案,意思是底层给了我们一个符合一定结构的,如上信息
//的字符串,通过给定的字符串转成相对应的对象结构,再获取值。
// AddressBookProto.Person person1;
// try {
// person1 =AddressBookProto.Person.parseFrom(person.toByteString());
// tv.setText(person1.getName());
// tv.setText((book.getPerson(0).getEmail()));
//
// } catch (InvalidProtocolBufferException e) {
// e.printStackTrace();
// }
tv.setText(book.getPerson(0).getEmail());
}
}
布局文件就是放了一个textview。
5、执行编译运行即可,默认会在app——》build——》generated——》source——》proto——》debug——》java (如果配置有cpp,大致意味是会产生c或c++相关的相应结构体)。此时可在java下看到和上方自定义的protobuf下面文件名+proto.java 类文件,如这里的Adressbook对应产生了AddressBookProto.java。因为文件很长,此处不再粘贴。
6、运行可看到,界面上打印出了一个我们在上面赋的值123