前言
Google 在I/O大會(huì)上宣布了官方支持kotlin作為android的開(kāi)發(fā)語(yǔ)言剂府。因此kotlin一下子進(jìn)入了android開(kāi)發(fā)同學(xué)們的實(shí)現(xiàn)掸掸,其實(shí)我很早就知道這門(mén)語(yǔ)言芹枷,也看了別人使用這門(mén)語(yǔ)言做android開(kāi)發(fā)庙楚,但是由于懶很魂,從來(lái)也沒(méi)有看過(guò)讶泰。正好借此機(jī)會(huì)咏瑟,大家都在趁熱乎勁學(xué)習(xí),我也翻開(kāi)了官方文檔看了看痪署,因?yàn)槲覍W(xué)習(xí)一門(mén)語(yǔ)言的方法就是簡(jiǎn)單瀏覽一下語(yǔ)法响蕴,然后直接開(kāi)始寫(xiě)一個(gè)簡(jiǎn)單的程序,這樣子在實(shí)現(xiàn)想要的功能的同時(shí)也就簡(jiǎn)單熟悉了一些常用的語(yǔ)法惠桃,于是就有了第一個(gè)練習(xí)的程序,也就有了這個(gè)分享辖试。
Kotlin優(yōu)點(diǎn)
Kotlin 的語(yǔ)法優(yōu)點(diǎn)還是很多的辜王。跟scala其實(shí)挺像的。如果寫(xiě)過(guò)scala罐孝,寫(xiě)這個(gè)會(huì)覺(jué)得挺順手的呐馆。
- 集合鏈?zhǔn)秸{(diào)用,像rxjava一樣莲兢,有很多的操作符汹来,寫(xiě)起來(lái)很舒服,語(yǔ)法糖比較多改艇,看起來(lái)邏輯也很清晰易讀收班。
- 代碼很精簡(jiǎn),很多可能在java中很復(fù)雜的代碼谒兄,在這里面就很精簡(jiǎn)摔桦,看起來(lái)很舒服。
例如我想把a(bǔ)ndroid項(xiàng)目中的一個(gè)源文件讀出來(lái)承疲,但是想把注釋掉的行排除掉邻耕,然后把整個(gè)文件轉(zhuǎn)化成一個(gè)String,并且用換行符連接起來(lái)燕鸽,那么簡(jiǎn)單的一行代碼的函數(shù)就可以搞定:
fun fileToString(fn: File) = fn.readLines().filter { l -> !l.trim().startsWith("http://") }.asSequence().joinToString("\n")
不必像Java一樣還得寫(xiě)好些輸入輸出流兄世,讀取再篩選再拼接。雖然Java經(jīng)過(guò)封裝也可以做到啊研,但是Kotlin直接就可以做這些操作非常的簡(jiǎn)單直觀(guān)御滩。這就是上面兩個(gè)優(yōu)點(diǎn)
- 兼容java,這個(gè)就是你以前在java中用的現(xiàn)在還是可以用,只是寫(xiě)法稍微有一單改動(dòng)悲伶。
舉個(gè)例子,我想拿到某個(gè)文件下所有以某些xml,png,jpg
啊這些結(jié)尾的文件的集合艾恼,那么給一個(gè)drawables
的集合作為extensions
參數(shù)給allFiles()這個(gè)函數(shù),java中常用的ArrayList
我們就直接用就可以了麸锉,減少了很多不適應(yīng)的成本钠绍。
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()
}
我體驗(yàn)到的大概就是這么多吧。其實(shí)也算不上什么新的優(yōu)點(diǎn)吧花沉,畢竟很多其他語(yǔ)言也是這樣的柳爽,可能對(duì)于平時(shí)寫(xiě)java的同學(xué)來(lái)說(shuō)確實(shí)感覺(jué)一下子清新了很多媳握,因?yàn)閖ava確實(shí)有很多繁雜的代碼。像我這樣的也只能有一點(diǎn)語(yǔ)法層面的感覺(jué)磷脯,畢竟我也是剛剛看了一會(huì)kotlin這個(gè)語(yǔ)言蛾找,沒(méi)有更多細(xì)致的了解。
實(shí)現(xiàn)
下面簡(jiǎn)單講一下如何實(shí)現(xiàn)這個(gè)功能的赵誓,雖然As貌似有這個(gè)功能打毛。但是我只是為了練習(xí)一下kotlin,也分享一下俩功。
流程很簡(jiǎn)單:
- 1.掃描項(xiàng)目中所有的資源文件幻枉。
也就是-hdpi", "-mdpi", "-xhdpi", "-xxhdpi", "-xxxhdpi"
這幾個(gè)文件夾和xml
資源放的文件夾中的png,jpg,xml這3種會(huì)在android項(xiàng)目中用到的資源文件。然后獲取他們的名字诡蜓,放到一個(gè)Set
里面熬甫,因?yàn)?code>Set集合中value不可重復(fù),每個(gè)文件夾下可能有相同名字的資源文件蔓罚,所以放到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()
這個(gè)功能實(shí)現(xiàn)很簡(jiǎn)單,就是掃描resDirs這幾個(gè)文件夾通過(guò)allFiles()
這個(gè)方法獲取文件夾下面所有的以drawables
這個(gè)集合中的字符結(jié)尾的文件豺谈。然后通過(guò)getFileName
獲取到文件的名字郑象,放到Set中
為什么要名字?
因?yàn)槲覀冊(cè)诖a中都是寫(xiě)R.mipmap.xxx
或者@mipmap/xxx
這樣的核无,我們要拿名字去和代碼中用到的名字比較匹配扣唱。
通過(guò)上面的代碼我們就拿到了項(xiàng)目中allNames
- 2.掃描項(xiàng)目中所有的代碼。
也就是.kt .java .xml
這3中文件中用到R.mipmap.xxx
或者@mipmap/xxx
的地方团南,解析出所有這些東西噪沙,就知道那些名字在項(xiàng)目中是出現(xiàn)過(guò)的。
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
集合里正歼。這個(gè)邏輯也挺簡(jiǎn)單的。跟上面一樣拷橘,首先通過(guò)allFiles()
掃描src
目錄下面所有以codes
集合中字符串結(jié)尾的文件局义,然后通過(guò)最上面提到的fileToString
方法把每個(gè)文件轉(zhuǎn)化成String
,通過(guò)正則匹配找出其中用到R.mipmap.xxx
或者@mipmap/xxx
的地方冗疮,再取出名字放到Set里面
萄唇。這樣我們就拿到了項(xiàng)目中用到過(guò)的所有的資源的名字usedNames
- 3
allNames
-usedNames
=unusedNames
這個(gè)道理就很簡(jiǎn)單了。
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) }
總結(jié)
短短50行代碼,很適合練習(xí)的一個(gè)小程序四敞,我花了大概幾個(gè)小時(shí)泛源,在沒(méi)寫(xiě)過(guò)kotlin的情況下寫(xiě)出來(lái)的,過(guò)程中感受了kotlin語(yǔ)法忿危,知道了一些大概的kotlin編程的方式达箍,也用了這些語(yǔ)法糖。雖然我感覺(jué)還是沒(méi)有scala簡(jiǎn)單铺厨,但是還是值得一試缎玫。希望給同樣是新手的你一點(diǎn)小的參考。
新手解滓,很多不正之處碘梢,還望指點(diǎn)。
所有代碼的地址:https://github.com/kingty/kotlin-script-demo/blob/master/clear.kts