ProtoBuf動(dòng)態(tài)拆分Gradle?Module解析
預(yù)期
當(dāng)前安卓的所有proto
都生成在一個(gè)module
中,但是其實(shí)業(yè)務(wù)同學(xué)需要的并不是一個(gè)大雜燴, 只需要其中他們所關(guān)心的proto
生成的類(lèi)則足以。所以我們希望能將這樣一個(gè)大雜燴的倉(cāng)庫(kù)打散,拆解成多個(gè)module
。
buf.yaml
Protobuf是Protocol Buffers的簡(jiǎn)稱(chēng),它是Google公司開(kāi)發(fā)的一種數(shù)據(jù)描述語(yǔ)言,用于描述一種輕便高效的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)格式,并于2008年對(duì)外開(kāi)源。Protobuf可以用于結(jié)構(gòu)化數(shù)據(jù)串行化,或者說(shuō)序列化。它的設(shè)計(jì)非常適用于在網(wǎng)絡(luò)通訊中的數(shù)據(jù)載體,很適合做數(shù)據(jù)存儲(chǔ)或 RPC 數(shù)據(jù)交換格式,它序列化出來(lái)的數(shù)據(jù)量少再加上以 K-V 的方式來(lái)存儲(chǔ)數(shù)據(jù),對(duì)消息的版本兼容性非常強(qiáng),可用于通訊協(xié)議、數(shù)據(jù)存儲(chǔ)等領(lǐng)域的語(yǔ)言無(wú)關(guān)、平臺(tái)無(wú)關(guān)、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)格式。開(kāi)發(fā)者可以通過(guò)Protobuf附帶的工具生成代碼并實(shí)現(xiàn)將結(jié)構(gòu)化數(shù)據(jù)序列化的功能。
在我司proto相關(guān)的都是由后端大佬們來(lái)維護(hù)的,然后這個(gè)協(xié)議倉(cāng)庫(kù)會(huì)被android/ios/后端/前端 依賴之后生成對(duì)應(yīng)的代碼,然后直接使用。
而proto文件中允許導(dǎo)入對(duì)于其他proto文件的依賴,所以這就導(dǎo)致了想要把幾個(gè)proto轉(zhuǎn)化成一個(gè)java-library
工程,還需要考慮依賴問(wèn)題。所以由 我們的后端來(lái)定義了一個(gè)buf.yaml
的數(shù)據(jù)格式。
version: v1 name: buf.xxx.co/xxx/xxxxxx deps: - buf.xxxxx.co/google/protobuf build: excludes: - setting breaking: use: - FILE lint: use: - DEFAULT
name
代表了這個(gè)工程的名字,deps
則表示了他依賴的proto的工程名?;谶@份yaml
內(nèi)容,我們就可以大概確定一個(gè)proto工程編譯需要的基礎(chǔ)條件。然后我們只需要一個(gè)工具或者插件來(lái)幫助我們生成對(duì)應(yīng)的工程就夠了。
模板工程
現(xiàn)在我們基本已經(jīng)有了一個(gè)單一的proto
工程的輸入模型了,工程名依賴的工程還有對(duì)應(yīng)文件夾下的proto
文件。然后我們就可以基于這部分輸入的模型,生成出第一個(gè)模板工程。
plugins { id 'java-library' id 'org.jetbrains.kotlin.jvm' id 'com.google.protobuf' } java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } sourceSets { def dirs = new ArrayList<String>() dirs.add("src/main/proto") main.proto.srcDirs = dirs } protobuf { protoc { if (System.getProperty("os.arch").compareTo("aarch64") == 0) { artifact = "com.google.protobuf:protoc:$version_protobuf_protoc:osx-x86_64" } else { artifact = "com.google.protobuf:protoc:$version_protobuf_protoc" } } plugins { grpc { if (System.getProperty("os.arch").compareTo("aarch64") == 0) { artifact = 'io.grpc:protoc-gen-grpc-java:1.36.1:osx-x86_64' } else { artifact = 'io.grpc:protoc-gen-grpc-java:1.36.1' } } } generateProtoTasks { all().each { task -> task.generateDescriptorSet = true task.builtins { // In most cases you don't need the full Java output // if you use the lite output. java { } } task.plugins { grpc { option 'lite' } } } } } afterEvaluate { project.tasks.findByName("compileJava").dependsOn(tasks.findByName("generateProto")) project.tasks.findByName("compileKotlin").dependsOn(tasks.findByName("generateProto")) } dependencies { implementation "org.glassfish:javax.annotation:10.0-b28" def grpcJava = '1.36.1' compileOnly "io.grpc:grpc-protobuf-lite:${grpcJava}" compileOnly "io.grpc:grpc-stub:${grpcJava}" compileOnly "io.grpc:grpc-core:${grpcJava}" File file = new File(projectDir, "depend.txt") if (!file.exists()) { return } def lines = file.readLines() if (lines.isEmpty()) { return } lines.forEach { logger.lifecycle("project:" + name + " implementation: " + it) implementation(it) } }
如果需要將proto
編譯成java代碼,就需要依賴于com.google.protobuf
插件,依賴于上面的build.gradle
基本就可以將一個(gè)proto
輸入編譯成一個(gè)jar
工程。
另外我們需要把所有的proto
文件拷貝到這個(gè)殼工程的src/main/proto
文件夾下,最后我們會(huì)將buf.yaml
中的name: buf.xxx.co/xxx/xxxxxx
的/xxx/xxxxxx
轉(zhuǎn)化成工程名,去除掉一些無(wú)法識(shí)別的字符。
我們生成的模板工程如下:
其中proto.version
會(huì)記錄proto
內(nèi)的gitsha
值還有文件的lastModified
時(shí)間,如果輸入發(fā)生變更則會(huì)重新進(jìn)行一次文件拷貝操作,避免重復(fù)覆蓋的風(fēng)險(xiǎn)。
input.txt
則包含了所有proto
文件路徑,方便我們進(jìn)行開(kāi)發(fā)調(diào)試。
deps 轉(zhuǎn)化
由于proto
之間存在依賴,沒(méi)有依賴則會(huì)導(dǎo)致無(wú)法將proto
轉(zhuǎn)化成java
。所以這里我講buf.yaml
中讀取出的deps
轉(zhuǎn)化成了一個(gè)depend.txt
.
com.xxxx.api:google-protobuf:7.7.7
depend.txt
內(nèi)會(huì)逐行寫(xiě)入當(dāng)前模塊的依賴,我們會(huì)對(duì)name進(jìn)行一次轉(zhuǎn)化,變成一個(gè)可讀的gradle
工程名。其中7.7.7
的版本只是一個(gè)缺省而已,并沒(méi)有實(shí)際的價(jià)值。
多線程操作
這里我們出現(xiàn)了一點(diǎn)點(diǎn)的性能問(wèn)題, 如果可以gradle
插件中盡量多使用點(diǎn)多線程,尤其是這種需要io
的操作中。
這里我通過(guò)ForkJoinPool
,這個(gè)是ExecutorService
的實(shí)現(xiàn)類(lèi)。其中submit
方法中會(huì)返回一個(gè)ForkJoinTask
,我們可以將獲取gitsha
值和lastModified
放在這個(gè)中。之后把所有的ForkJoinTask
放到一個(gè)數(shù)組中。
fun await() { forkJoins.forEach { it.join() } }
然后最后暴露一個(gè)await
方法,來(lái)做到所有的獲取方法完成之后再繼續(xù)向下執(zhí)行。
另外則就是殼module的生成,我們也放在了子線程內(nèi)執(zhí)行。我們這次使用了線程池的invokeAll
方法。
protoFileWalk.hashMap.forEach { (_, pbBufYaml) -> callables.add(Callable<Void> { val root = FileUtils.getRootProjectDir(settings.gradle) try { val file = pbBufYaml.copyLib(File(root, "bapi")) projects[pbBufYaml.projectName()] = file.absolutePath ?: "" } catch (e: Exception) { e.printStackTrace() e.message.log() } null }) } executor.invokeAll(callables)
這里有個(gè)面試經(jīng)常出現(xiàn)的考點(diǎn),多線程操作Hashmap
,之后我在測(cè)試環(huán)節(jié)隨機(jī)出現(xiàn)了生成工程和include不匹配的問(wèn)題。所以最后我更換了ConcurrentHashMap
就沒(méi)有出現(xiàn)這個(gè)問(wèn)題了。
加載殼Module
這部分就和源碼編譯插件基本是一樣的寫(xiě)法。
projects.forEach { (s, file) -> settings.include(":${s}") settings.project(":${s}").projectDir = File(file) }
把工程插入settings 即可。
結(jié)尾
這部分方案這樣也就大概完成了一半,剩下的一半我們需要逐一把生層業(yè)務(wù)的依賴進(jìn)行一次變更,這樣就可以做到依賴最小化,然后也可以去除掉一部分無(wú)用的代碼塊。
以上就是ProtoBuf動(dòng)態(tài)拆分Gradle Module解析的詳細(xì)內(nèi)容,更多關(guān)于ProtoBuf拆分Gradle Module的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android中利用動(dòng)態(tài)加載實(shí)現(xiàn)手機(jī)淘寶的節(jié)日特效
這篇文章主要介紹了Android中利用動(dòng)態(tài)加載實(shí)現(xiàn)手機(jī)淘寶的節(jié)日特效,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-12-12Android實(shí)現(xiàn)狀態(tài)欄(statusbar)漸變效果的示例
本篇文章主要介紹了Android實(shí)現(xiàn)狀態(tài)欄(statusbar)漸變效果的示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09Android自定義ViewGroup實(shí)現(xiàn)淘寶商品詳情頁(yè)
這篇文章主要為大家詳細(xì)介紹了Android自定義ViewGroup實(shí)現(xiàn)淘寶商品詳情頁(yè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10Android開(kāi)發(fā)中ImageView的scaletype屬性用法分析
這篇文章主要介紹了Android開(kāi)發(fā)中ImageView的scaletype屬性用法,分析了scaletype屬性參數(shù)的常見(jiàn)功能并結(jié)合實(shí)例形式給出了具體的使用方法,需要的朋友可以參考下2016-08-08使用Flutter開(kāi)發(fā)一個(gè)圖片UI組件的代碼示例
在移動(dòng)應(yīng)用開(kāi)發(fā)中,圖片展示是一個(gè)常見(jiàn)的需求,為了滿足不同場(chǎng)景的圖片展示需求,我們可以開(kāi)發(fā)一個(gè)靈活配置的圖片UI組件,本文將介紹如何使用Flutter開(kāi)發(fā)一個(gè)圖片UI組件,并提供了豐富的配置選項(xiàng),需要的朋友可以參考下2023-09-09Retrofit 創(chuàng)建網(wǎng)絡(luò)請(qǐng)求接口實(shí)例過(guò)程
這篇文章主要為大家介紹了Retrofit 創(chuàng)建網(wǎng)絡(luò)請(qǐng)求接口實(shí)例過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Android應(yīng)用中通過(guò)Layout_weight屬性用ListView實(shí)現(xiàn)表格
這篇文章主要介紹了Android應(yīng)用中通過(guò)Layout_weight屬性用ListView實(shí)現(xiàn)表格的方法,文中對(duì)Layout_weight屬性先有一個(gè)較為詳細(xì)的解釋,需要的朋友可以參考下2016-04-04