用kotlin来开发一个cli工具 | 没用的技能+1

脚手架

脚手架是为了保证各施工过程顺利进行而搭设的工作平台

而在程序开发过程中,每个工程或者说公司也都需要一个脚手架工具。通过脚手架命令行的形式简化开发流程,避免发生一些人为的相对低级的问题,所以这个也就是为什么叫做脚手架的原因吧。

而由于每个公司的代码规范都不同,一般情况下会主动让开发同学进行工程方面的cv操作,就是成本高并且容易出错。这也就是为什么我们打算写一些这样的工具的原因。

在一般情况下,更多的程序猿会选择用python去写,因为脚本语言的灵活性,但是对于一个辣鸡安卓来说会增加额外的学习成本,所以这就取决于有没有天赋了,能不能对一门陌生的语言快速上手了。

这次文章会介绍的是用kotlin去构建一个二进制文件,通过这个来完成脚手架cli工具的建设。

开搞

demo 工程地址TheNext

一开始的启发在于有时候使用一些第三方工具的时候会提供一个jar包,然后只要输入java -jar xxx.jar就可以使用这个jar包中的Main函数了。

因为是一个jar包,所以里面的内容肯定也都是用jvm内的几种语言来进行编写的,那么这就让我们这种老年选手看到了一丝丝的希望。

开发调试

先建立了一个java工程,然后构建了一个main函数,之后开始进行代码编写。但是如果每次都需要先打包之后在通过java -jar来执行的话非常不便利开发并且debug。而且模拟入参也灰常的恶心,你也知道的程序猿都是懒人吗。

所以我们就借用了unittest的能力,对于入参进行mock进行简单的调试功能了。

【参考地址](github.com/Leifzhang/T…)

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函数通过unittest来进行模拟,这样就可以方便我们在开发阶段快速调试脚手架的能力了。

每个方法块都可以认为是一个运行的入口,通过这个来模拟出程序所需要的入参。从而一边完成了测试代码的编写,一边完成了调试入口。

jcommander

这是一个让我们可以更像模像样的写一个cli的入参解析工具,即使参数顺序是错乱的,我们仍然能解析出我们想要的数据结构,让我们的工程看起来更正规一点。而且这个库也被很多开源项目所使用,基本算的上是千锤百炼了,比如美团的walle

jcommander值得你一个star的

@Parameters(commandDescription = "args 参数")
class CommandEntity {

    @Parameter(
        names = ["-file", "-f"],
        required = true,
        converter = FileConverter::class,
        description = "生成目标文件路径"
    )
    lateinit var file: File

    @Parameter(
        names = ["-name"], required = true,
        description = "文件名"
    )
    lateinit var name: String

    @Parameter(names = ["-group", "-bundle", "-g", "-b"], description = "唯一标识符")
    var group: String? = null

}


override fun handle(args: Array<String>) {
 val commandEntity = CommandEntity()
 JCommander.newBuilder().addObject(commandEntity).build().parse(*args)
}

实例demo如上,我也是参考了官方demo写的。通过JCommander将args解析成对应的数据实体结构。

Main 函数声明

我们要在build.gradle内的jar的task中,声明当前jar的main函数,作为命令行工具的入口。否则打出来的jar包就会报没有main函数的异常。

jar {
    exclude("**/module-info.class")
    /* from {
         configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
     }*/
    manifest {
        attributes 'Main-Class': 'com.kronos.mebium.Next'
    }
}

其中from的含义就是将一个jar包把所有的依赖都打到一起,从而形成一个fatjar,而后续因为使用了gradle提供的application插件,所以这行被我注释了。

压缩模板

我们这个脚手架最核心的就是把一部分工程模板压缩成一个zip资源文件,打包带入jar产物中。然后呢我这个人又比较懒,希望每次执行打包的时候都进行一次模板的压缩替换,所以这里我通过一部分gradle task来进行执行了。

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
    }
}

首先定义出一个task,然后定义好输入输出,输入的是一个文件夹,输出的则是一个zip的压缩文件,输入输出的地址由外部来声明。


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)
}

然后直接声明处两个task,之后把compileJava依赖到这两个task上去,这样就可以保证每次compileJava,这两个task都会被执行到了。编译缓存我就不说了,大家自行领悟吧。

java resource 读取方式 javaClass.classLoader.getResourceAsStream(name) 就可以了。

放飞自我

接下来我们就可以在命令行工具内放飞自我,开始很简单的通过unittest来进行代码的编写和调试了。

我们就可以通过自己熟悉的kotlin或者java来编写一个简单的cli工具,从而来进一步的做到基于工程定制化的一些方便的脚手架工具了。

生成最终产物

这里我们使用了 gradle提供的application plugin,这个插件可以将java jar包装成一个可执行文件的zip的压缩包。格式如下图所示:

而这个的生成指令就是,通过./gradlew impact:assembleDist 任务生成对应的二进制压缩包。

这样的好处就是我们可以省略掉java -jar xxxxx.jar的繁琐操作,通过可执行文件直接达到我们写一个cli的便利。

结尾

工程内的代码还是比较简单的,有兴趣的就自己读一下,只是一个demo而已。

还是那句因为菜,不想去学一门新语言。如果万一哪怕我的py在强那么一点点,我也考虑用py来写了,哈哈哈哈哈。

作者:究极逮虾户
链接:https://juejin.cn/post/7171248904971747341

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

在这里插入图片描述

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

在这里插入图片描述

五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
在这里插入图片描述

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

在这里插入图片描述

七、Flutter技术进阶

1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)

在这里插入图片描述

八、微信小程序开发

1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……

在这里插入图片描述

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,可扫描下方CSDN官方认证微信二维码免费领取↓↓↓

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值