Kotlin 50行代码实现清除Android项目中不用的图片和资源

前言

Google 在I/O大会上宣布了官方支持kotlin作为android的开发语言。因此kotlin一下子进入了android开发同学们的实现,其实我很早就知道这门语言,也看了别人使用这门语言做android开发,但是由于懒,从来也没有看过。正好借此机会,大家都在趁热乎劲学习,我也翻开了官方文档看了看,因为我学习一门语言的方法就是简单浏览一下语法,然后直接开始写一个简单的程序,这样子在实现想要的功能的同时也就简单熟悉了一些常用的语法,于是就有了第一个练习的程序,也就有了这个分享。

Kotlin优点

Kotlin 的语法优点还是很多的。跟scala其实挺像的。如果写过scala,写这个会觉得挺顺手的。

  • 集合链式调用,像rxjava一样,有很多的操作符,写起来很舒服,语法糖比较多,看起来逻辑也很清晰易读。
  • 代码很精简,很多可能在java中很复杂的代码,在这里面就很精简,看起来很舒服。
    例如我想把android项目中的一个源文件读出来,但是想把注释掉的行排除掉,然后把整个文件转化成一个String,并且用换行符连接起来,那么简单的一行代码的函数就可以搞定:
fun fileToString(fn: File) = fn.readLines().filter { l -> !l.trim().startsWith("//") }.asSequence().joinToString("\n")

不必像Java一样还得写好些输入输出流,读取再筛选再拼接。虽然Java经过封装也可以做到,但是Kotlin直接就可以做这些操作非常的简单直观。这就是上面两个优点

  • 兼容java,这个就是你以前在java中用的现在还是可以用,只是写法稍微有一单改动。
    举个例子,我想拿到某个文件下所有以某些xml,png,jpg啊这些结尾的文件的集合,那么给一个drawables的集合作为extensions参数给allFiles()这个函数,java中常用的ArrayList我们就直接用就可以了,减少了很多不适应的成本。
val drawables = sequenceOf("png", "jpg", "xml")
fun allFiles(f: File, extensions: Sequence<String>): Sequence<File> {
    val files = ArrayList<File>()
    if (f.isDirectory) {
        f.listFiles().map { f -> files.addAll(allFiles(f, extensions)) }
    } else if (extensions.any { ex -> f.name.endsWith("." + ex) }) {
        files.add(f)
    }
    return files.asSequence()
}

我体验到的大概就是这么多吧。其实也算不上什么新的优点吧,毕竟很多其他语言也是这样的,可能对于平时写java的同学来说确实感觉一下子清新了很多,因为java确实有很多繁杂的代码。像我这样的也只能有一点语法层面的感觉,毕竟我也是刚刚看了一会kotlin这个语言,没有更多细致的了解。

实现

下面简单讲一下如何实现这个功能的,虽然As貌似有这个功能。但是我只是为了练习一下kotlin,也分享一下。
流程很简单:

  • 1.扫描项目中所有的资源文件。
    也就是-hdpi", "-mdpi", "-xhdpi", "-xxhdpi", "-xxxhdpi"这几个文件夹和xml资源放的文件夹中的png,jpg,xml这3种会在android项目中用到的资源文件。然后获取他们的名字,放到一个Set里面,因为Set集合中value不可重复,每个文件夹下可能有相同名字的资源文件,所以放到Set里去重。
fun getFileName(f: File): String {
    return f.name.replace(".9.png", "").replace(".png", "").replace(".jpg", "").replace(".xml", "")
}
val drawables = sequenceOf("png", "jpg", "xml")
val resDirs = sequenceOf("-hdpi", "-mdpi", "-xhdpi", "-xxhdpi", "-xxxhdpi").map { s -> "app/src/main/res/mipmap$s" }.asSequence()
fun reses(): Sequence<File> = resDirs.map { str -> allFiles(File(str), drawables) }.flatten()
fun allNames() = reses().map { file -> getFileName(file) }.toSet()

这个功能实现很简单,就是扫描resDirs这几个文件夹通过allFiles()这个方法获取文件夹下面所有的以drawables这个集合中的字符结尾的文件。然后通过getFileName获取到文件的名字,放到Set中
为什么要名字?
因为我们在代码中都是写R.mipmap.xxx或者@mipmap/xxx这样的,我们要拿名字去和代码中用到的名字比较匹配。
通过上面的代码我们就拿到了项目中allNames

  • 2.扫描项目中所有的代码。
    也就是.kt .java .xml这3中文件中用到R.mipmap.xxx或者@mipmap/xxx的地方,解析出所有这些东西,就知道那些名字在项目中是出现过的。
val pattern = """@mipmap\/[a-zA-Z0-9_]+[^a-zA-Z0-9_]|R\.mipmap\.[A-Za-z0-9_]+[^a-zA-Z0-9_]""".toPattern()
val codes = sequenceOf("kt", "java", "xml")
val projects = sequenceOf("app")
val usedNames = projects.map { p ->
    allFiles(File(p + "/src"), codes).map { f -> fileToString(f) }.map { str ->
        val names = ArrayList<String>()
        val m = pattern.matcher(str)
        while (m.find()) {
            names.add(m.toMatchResult().group().dropLast(1).replace("@mipmap/", "").replace("R.mipmap.", ""))
        }
        names.asSequence()
    }.flatten()
}.flatten().toSet()

如果你还有其他的module,可以加到projects集合里。这个逻辑也挺简单的。跟上面一样,首先通过allFiles()扫描src目录下面所有以codes集合中字符串结尾的文件,然后通过最上面提到的fileToString方法把每个文件转化成String,通过正则匹配找出其中用到R.mipmap.xxx或者@mipmap/xxx的地方,再取出名字放到Set里面。这样我们就拿到了项目中用到过的所有的资源的名字usedNames

  • 3 allNames - usedNames = unusedNames
    这个道理就很简单了。
val unusedNames = allNames().filter { d -> !usedNames.contains(d) }.toSet()
  • 4 遍历资源文件夹,找到和unusedNames相同名字的文件,删除。
fun cleanDir(f: File) {
    if (f.isDirectory) {
        f.listFiles().forEach { f -> cleanDir(f) }
    } else {
        if (unusedNames.map { s -> getFileName(f) == s }.toSet().contains(true)) {
            f.delete()
        }
    }
}
fun clean() = resDirs.map { s -> File(s) }.forEach { f -> cleanDir(f) }

总结

短短50行代码,很适合练习的一个小程序,我花了大概几个小时,在没写过kotlin的情况下写出来的,过程中感受了kotlin语法,知道了一些大概的kotlin编程的方式,也用了这些语法糖。虽然我感觉还是没有scala简单,但是还是值得一试。希望给同样是新手的你一点小的参考。
新手,很多不正之处,还望指点。
所有代码的地址:https://github.com/kingty/kotlin-script-demo/blob/master/clear.kts

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容