kotlin開(kāi)發(fā)cli工具小技巧詳解
腳手架
腳手架是為了保證各施工過(guò)程順利進(jìn)行而搭設(shè)的工作平臺(tái)
而在程序開(kāi)發(fā)過(guò)程中,每個(gè)工程或者說(shuō)公司也都需要一個(gè)腳手架工具。通過(guò)腳手架命令行的形式簡(jiǎn)化開(kāi)發(fā)流程,避免發(fā)生一些人為的相對(duì)低級(jí)的問(wèn)題,所以這個(gè)也就是為什么叫做腳手架的原因吧。
而由于每個(gè)公司的代碼規(guī)范都不同,一般情況下會(huì)主動(dòng)讓開(kāi)發(fā)同學(xué)進(jìn)行工程方面的cv操作,就是成本高并且容易出錯(cuò)。這也就是為什么我們打算寫(xiě)一些這樣的工具的原因。
在一般情況下,更多的程序猿會(huì)選擇用python去寫(xiě),因?yàn)槟_本語(yǔ)言的靈活性,但是對(duì)于一個(gè)辣雞安卓來(lái)說(shuō)會(huì)增加額外的學(xué)習(xí)成本,所以這就取決于有沒(méi)有天賦了,能不能對(duì)一門(mén)陌生的語(yǔ)言快速上手了。
這次文章會(huì)介紹的是用kotlin去構(gòu)建一個(gè)二進(jìn)制文件,通過(guò)這個(gè)來(lái)完成腳手架cli工具的建設(shè)。
開(kāi)搞
demo 工程地址 TheNext
一開(kāi)始的啟發(fā)在于有時(shí)候使用一些第三方工具的時(shí)候會(huì)提供一個(gè)jar包,然后只要輸入java -jar xxx.jar
就可以使用這個(gè)jar包中的Main函數(shù)了。
因?yàn)槭且粋€(gè)jar包,所以里面的內(nèi)容肯定也都是用jvm內(nèi)的幾種語(yǔ)言來(lái)進(jìn)行編寫(xiě)的,那么這就讓我們這種老年選手看到了一絲絲的希望。
開(kāi)發(fā)調(diào)試
先建立了一個(gè)java工程,然后構(gòu)建了一個(gè)main函數(shù),之后開(kāi)始進(jìn)行代碼編寫(xiě)。但是如果每次都需要先打包之后在通過(guò)java -jar
來(lái)執(zhí)行的話非常不便利開(kāi)發(fā)并且debug。而且模擬入?yún)⒁不页5膼盒模阋仓赖某绦蛟扯际菓腥藛帷?/p>
所以我們就借用了unittest
的能力,對(duì)于入?yún)⑦M(jìn)行mock進(jìn)行簡(jiǎn)單的調(diào)試功能了。
class Sample { @Test fun help() { Next.main( arrayOf( "--help" ) ) } @Test fun testAndroidModule() { val file = File("") val moduleName = "strike-freedom" val groupName = "com.kronos.common" Next.main( arrayOf( "module", "android", "-file", file.absolutePath, "-name", moduleName, "-group", groupName ) ) } @Test fun testAndroidApplication() { val file = File("../app/") val projectName = "freedom" Next.main( arrayOf( "project", "android", "-name", projectName, "-file", file.absolutePath ) ) } }
此處我們將Main函數(shù)通過(guò)unittest來(lái)進(jìn)行模擬,這樣就可以方便我們?cè)陂_(kāi)發(fā)階段快速調(diào)試腳手架的能力了。
每個(gè)方法塊都可以認(rèn)為是一個(gè)運(yùn)行的入口,通過(guò)這個(gè)來(lái)模擬出程序所需要的入?yún)ⅰ亩贿呁瓿闪藴y(cè)試代碼的編寫(xiě),一邊完成了調(diào)試入口。
jcommander
這是一個(gè)讓我們可以更像模像樣的寫(xiě)一個(gè)cli的入?yún)⒔馕龉ぞ?,即使參?shù)順序是錯(cuò)亂的,我們?nèi)匀荒芙馕龀鑫覀兿胍臄?shù)據(jù)結(jié)構(gòu),讓我們的工程看起來(lái)更正規(guī)一點(diǎn)。而且這個(gè)庫(kù)也被很多開(kāi)源項(xiàng)目所使用,基本算的上是千錘百煉了,比如美團(tuán)的walle
。
jcommander值得你一個(gè)star的
@Parameters(commandDescription = "args 參數(shù)") class CommandEntity { @Parameter( names = ["-file", "-f"], required = true, converter = FileConverter::class, description = "生成目標(biāo)文件路徑" ) lateinit var file: File @Parameter( names = ["-name"], required = true, description = "文件名" ) lateinit var name: String @Parameter(names = ["-group", "-bundle", "-g", "-b"], description = "唯一標(biāo)識(shí)符") var group: String? = null }
override fun handle(args: Array<String>) { val commandEntity = CommandEntity() JCommander.newBuilder().addObject(commandEntity).build().parse(*args) }
實(shí)例demo如上,我也是參考了官方demo寫(xiě)的。通過(guò)JCommander
將args解析成對(duì)應(yīng)的數(shù)據(jù)實(shí)體結(jié)構(gòu)。
Main 函數(shù)聲明
我們要在build.gradle
內(nèi)的jar的task中,聲明當(dāng)前jar的main
函數(shù),作為命令行工具的入口。否則打出來(lái)的jar包就會(huì)報(bào)沒(méi)有main函數(shù)的異常。
jar { exclude("**/module-info.class") /* from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }*/ manifest { attributes 'Main-Class': 'com.kronos.mebium.Next' } }
其中from的含義就是將一個(gè)jar包把所有的依賴都打到一起,從而形成一個(gè)fatjar
,而后續(xù)因?yàn)槭褂昧薵radle提供的application
插件,所以這行被我注釋了。
壓縮模板
我們這個(gè)腳手架最核心的就是把一部分工程模板壓縮成一個(gè)zip資源文件,打包帶入jar產(chǎn)物中。然后呢我這個(gè)人又比較懶,希望每次執(zhí)行打包的時(shí)候都進(jìn)行一次模板的壓縮替換,所以這里我通過(guò)一部分gradle task
來(lái)進(jìn)行執(zhí)行了。
abstract class ZipTask extends DefaultTask { @InputDirectory Provider<File> library = project.objects.property(File) @OutputFile Provider<File> outputFile = project.objects.property(File) @TaskAction def doAction() { def outputFile = outputFile.get() createFileSafety(outputFile) compress(library.get(), outputFile) } static File compress(final File srcDir, final File zipFile) { ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile)) srcDir.eachFileRecurse({ zos.putNextEntry(new ZipEntry(it.path - srcDir.path + (it.directory ? "/" : ""))) if (it.file) { zos << it.bytes } zos.closeEntry() }) zos.close() return zipFile } private static File createFileSafety(File file) { if (file.exists()) { file.delete() } if (!file.getParentFile().exists()) { file.getParentFile().mkdirs() } return file } }
首先定義出一個(gè)task,然后定義好輸入輸出,輸入的是一個(gè)文件夾,輸出的則是一個(gè)zip的壓縮文件,輸入輸出的地址由外部來(lái)聲明。
def moduleTask = project.tasks.register("zipAndroidLib", ZipTask.class) { it.library.set(file("../library")) it.outputFile.set(file("./src/main/resources/zip/android/android.zip")) } def projectTask = project.tasks.register("zipAndroidProject", ZipTask.class) { it.library.set(file("../project")) it.outputFile.set(file("./src/main/resources/zip/android/project.zip")) } afterEvaluate { project.tasks.findByName("compileJava").dependsOn(moduleTask) project.tasks.findByName("compileJava").dependsOn(projectTask) }
然后直接聲明處兩個(gè)task,之后把compileJava
依賴到這兩個(gè)task上去,這樣就可以保證每次compileJava
,這兩個(gè)task都會(huì)被執(zhí)行到了。編譯緩存我就不說(shuō)了,大家自行領(lǐng)悟吧。
java resource 讀取方式 javaClass.classLoader.getResourceAsStream(name) 就可以了。
放飛自我
接下來(lái)我們就可以在命令行工具內(nèi)放飛自我,開(kāi)始很簡(jiǎn)單的通過(guò)unittest來(lái)進(jìn)行代碼的編寫(xiě)和調(diào)試了。
我們就可以通過(guò)自己熟悉的kotlin或者java來(lái)編寫(xiě)一個(gè)簡(jiǎn)單的cli工具,從而來(lái)進(jìn)一步的做到基于工程定制化的一些方便的腳手架工具了。
生成最終產(chǎn)物
這里我們使用了 gradle提供的application plugin
,這個(gè)插件可以將java jar包裝成一個(gè)可執(zhí)行文件的zip的壓縮包。格式如下圖所示:
而這個(gè)的生成指令就是,通過(guò)./gradlew impact:assembleDist 任務(wù)生成對(duì)應(yīng)的二進(jìn)制壓縮包。
這樣的好處就是我們可以省略掉java -jar xxxxx.jar
的繁瑣操作,通過(guò)可執(zhí)行文件直接達(dá)到我們寫(xiě)一個(gè)cli的便利。
結(jié)尾
工程內(nèi)的代碼還是比較簡(jiǎn)單的,有興趣的就自己讀一下,只是一個(gè)demo而已。
還是那句因?yàn)椴耍幌肴W(xué)一門(mén)新語(yǔ)言。如果萬(wàn)一哪怕我的py在強(qiáng)那么一點(diǎn)點(diǎn),我也考慮用py來(lái)寫(xiě)了,哈哈哈哈哈。
以上就是kotlin開(kāi)發(fā)cli工具小技巧詳解的詳細(xì)內(nèi)容,更多關(guān)于kotlin開(kāi)發(fā)cli工具的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
android 跳轉(zhuǎn)進(jìn)市場(chǎng)的實(shí)現(xiàn)代碼
本篇文章是對(duì)android中跳轉(zhuǎn)進(jìn)市場(chǎng)的實(shí)現(xiàn)代碼進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06在Android線程池里運(yùn)行代碼任務(wù)實(shí)例
這篇文章主要介紹了在Android線程池里運(yùn)行代碼任務(wù)實(shí)例,同時(shí)介紹了線程池中停止任務(wù)的方法,需要的朋友可以參考下2014-06-06Android用TextView實(shí)現(xiàn)跑馬燈效果代碼
大家好,本篇文章主要講的是Android?TextView實(shí)現(xiàn)跑馬燈效果代碼,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01android下拉刷新ListView的介紹和實(shí)現(xiàn)代碼
在當(dāng)下,列表組件不帶下拉刷新的都不好意思叫列表。第一次完成列表的下拉刷新功能的時(shí)候,直接在Activity中實(shí)現(xiàn),雖然功能上是實(shí)現(xiàn)了,總體上感覺(jué)很亂。所以第二次用到的時(shí)候,就想著封裝成一個(gè)組件,實(shí)現(xiàn)和Activity的解耦。2013-04-04Flutter配置代理抓包實(shí)現(xiàn)過(guò)程詳解
這篇文章主要為大家介紹了Flutter配置代理抓包實(shí)現(xiàn)過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02基于Android 實(shí)現(xiàn)圖片平移、縮放、旋轉(zhuǎn)同時(shí)進(jìn)行
這篇文章主要介紹了基于Android 實(shí)現(xiàn)圖片平移、縮放、旋轉(zhuǎn)同時(shí)進(jìn)行的相關(guān)資料,需要的朋友可以參考下2015-11-11Android 中RecyclerView通用適配器的實(shí)現(xiàn)
這篇文章主要介紹了Android 中RecyclerView通用適配器的實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2017-03-03Android實(shí)現(xiàn)可復(fù)用的篩選頁(yè)面
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)可復(fù)用的篩選頁(yè)面,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06