name=zx1001.txt
absoluteFile=/storage/emulated/0/Android/data/zx.com.demo/files/zx1001.txt
absolutePath=/storage/emulated/0/Android/data/zx.com.demo/files/zx1001.txt
path=/storage/emulated/0/Android/data/zx.com.demo/files/zx1001.txt
parent=/storage/emulated/0/Android/data/zx.com.demo/files
重命名是否成功false,name=zx1001.txt

文件检测

val file = File(getExternalFilesDir(“”)?.absolutePath + “/app-debug.apk”)
val dir = File(getExternalFilesDir(“”)?.absolutePath)
println(“exists=” + file.exists())
println(“canExecute=” + file.canExecute())
println(“canRead=” + file.canRead())
println(“canWrite=” + file.canWrite())
println(“isDirectory=” + file.isDirectory)
println(“lastModified=” + Date(file.lastModified()))
println(“length=” + file.length())
println(“---------”)
println(“dir.isDirectory=” + dir.isDirectory)
println(“dir.lastModified=” + Date(dir.lastModified()))
println(“dir.length=” + dir.length())

输出

exists=true
canExecute=false
canRead=true
canWrite=true
isDirectory=false
lastModified=Fri Jun 11 14:05:38 GMT+08:00 2021
length=7785185

dir.isDirectory=true
dir.lastModified=Fri Jun 11 14:13:14 GMT+08:00 2021
dir.length=4096

注意.length()只能返回文件的大小(单位为字节),文件夹的实际大小无法返回。

文件夹操作

val dir = File(getExternalFilesDir(“”)?.absolutePath + “/myDir/dir1”)
dir.mkdirs()//创建一个文件目录。若上层文件目录不存在,一并创建

val appDir = File(getExternalFilesDir(“”)?.absolutePath)
dir.mkdirs()

val files = appDir.listFiles() //返回目录下的文件列表
for (i in files) {
println(i)
}

val filePaths = appDir.list()//返回目录下文件名和文件夹名称数组
for (i in filePaths) {
println(i)
}

输出

/storage/emulated/0/Android/data/zx.com.demo/files/myDir
/storage/emulated/0/Android/data/zx.com.demo/files/zx1001.txt
/storage/emulated/0/Android/data/zx.com.demo/files/app-debug.apk
myDir
zx1001.txt
app-debug.apk

kotlin对File的扩展

以上这些示例中用的都是Java提供的API,kotlin提供了一些扩展方法和扩展属性,可以更加方便的操作文件。

名称 作用
extension 文件格式的后缀,不包括.,例如:mp3
nameWithoutExtension 名称,不包括后缀
appendBytes(ByteArray) 追加字节数组到文件末尾
writeBytes(ByteArray) 写入字节数组,如果之前有内容,会被覆盖
readBytes(): ByteArray 以字节数组的形式获取此文件的全部内容,不建议在大文件上使用此方法,上限2G
appendText(String,Charset) 追加字符串到文件末尾,默认是UTF-8编码
writeText(String,Charset) 写入字符串,如果之前有内容,会被覆盖
readText(Charset): String 以String 形式获取此文件的全部内容,默认编码UTF-8,不建议在大文件上使用此方法,上限2G
reader(Charset): InputStreamReader 返回一个FileReader以读取此文件的内容
bufferedReader(Charset, bufferSize: Int): BufferedReader 返回一个BufferedReader用于读取此文件的内容
writer(Charset):OutputStreamWriter 返回一个FileWriter用于写入此文件
bufferedWriter(Charset, bufferSize: Int): BufferedWriter 返回一个BufferedWriter用于写入此文件
forEachBlock(action: (buffer: ByteArray, bytesRead: Int) -> Unit) 高阶函数,按字节块读取文件并为每个读取的块调用action,字节块默认为4096。适用于大文件
forEachLine(Charset, action: (line: String) -> Unit) 高阶函数,按行读取文件并为每行调用action,适用于大文件
readLines( Charset): List 按行读取文件,不要在大文件上使用此方法
copyTo(target: File, overwrite: Boolean = false, bufferSize: Int = DEFAULT_BUFFER_SIZE): File 复制文件或者文件夹,并且会创建target所需的各个父级文件夹(如果缺少)。overwrite为true时,target可被覆盖,不为true并且当target存在时,返回false,复制失败。overwrite为true并且target是一个文件夹时,只有当文件夹为空时才会被替换。源文件如果是文件夹,则只会创建目标文件夹,不会复制文件夹中的文件。该操作不会保留复制的文件属性,例如创建/修改日期、权限等
copyRecursively(target: File, overwrite: Boolean = false, onError: (File, IOException)) 递归复制文件或者文件夹,并且会创建target所需的各个父级文件夹(如果缺少)。如果源文件是文件夹,将复制文件夹中的所有内容。该操作不会保留复制的文件属性,例如创建/修改日期、权限等。默认自带的错误处理器会将错误抛出,可以传入一个Lambda用来处理异常。复制文件夹失败时,可能已经复制了一部分
deleteRecursively() 递归删除文件或者文件夹,删除文件夹失败时,可能已经删除了一部分
.walk() 自上而下,深度优先,遍历整个文件夹

看完这些扩展方法,瞬间觉得Java不香了,感觉都不需要再学IO流了,但是有时候我们并不是只处理文件的IO流,所以Java的IO流还是要学。

IO流

按照“流”的数据流向,可以将其化分为:输入流和输出流。输入和输出是相对于程序而言,数据流入程序中,那就是输入流,数据流出程序,那就是输出流。按照“流”中处理数据的单位,可以将其区分为:字节流(二进制流)和字符流。在java中,字节是占1个Byte,即8位;而字符是占2个Byte,即16位。一般来说, 对于文本编辑器或文本输出程序创建的文件例如.txt、.xml、.java,应该使用字符流来读取, 对于二进制文件例如png、doc, 应该使用字节流来读取。因为字节流操作的是二进制数据,所以字节流不涉及编码。字符流操作的是字符,而字符是有编码的,所以字符流需要指定编码,下方的例子中都是用的默认的UTF-8编码。

字节流的抽象基类:InputStream,OutputStream。字符流的抽象基类:Reader,Writer。

由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀,如InputStream的子类FileInputStream,Reader的子类FileReader。

表格总结了各种流:

IO流.png

文件流

文件流可以直接操作文件的读写,字符流操作的类为FileWriter和FileReader,字节流操作的类为FileInputStream和FileOutputStream。

FileWriter、FileReader

val fileWriter = FileWriter(file, true)
fileWriter.write(SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(Date()) + “,自己换行\n”)
fileWriter.appendLine(SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(Date()) + “,张三”)
fileWriter.close()

var reader = FileReader(file)
val buf = CharArray(1024)
var len: Int
while (reader.read(buf).also { len = it } != -1) {
println(String(buf, 0, len))
}
reader.close()
println(“-------------”)

//使用扩展方法
reader = FileReader(file)
val lines = reader.readLines()
for (item in lines) {
println(item)
}
println(“-------------”)

//使用扩展方法
reader = FileReader(file)
reader.forEachLine {
println(it)
}
println(“-------------”)

//使用扩展方法
reader = FileReader(file)
reader.useLines {
it.iterator().forEach { it1 ->
println(it1)
}
}

FileWriter的第二个参数,表示是否追加写入,true为追加,false为覆盖。write中可以追加\n实现换行,也可以使用kotlin扩展函数appendLine实现写入一行。读数据时,同样可以使用kotlin扩展函数readLines()、forEachLine、useLines来简化读取,这三种读取方式不需要手动关闭流。最终输出:

2021-06-11 22:43:18,自己换行
2021-06-11 22:43:18,张三

2021-06-11 22:43:18,自己换行
2021-06-11 22:43:18,张三

2021-06-11 22:43:18,自己换行
2021-06-11 22:43:18,张三

2021-06-11 22:43:18,自己换行
2021-06-11 22:43:18,张三

FileInputStream、FileOutputStream

val fileOutputStream = FileOutputStream(file, true)
fileOutputStream.write((SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(Date()) + “,自己换行\n”).toByteArray())
fileOutputStream.write((SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”).format(Date()) + “,李四\n”).toByteArray())

var fileInputStream = FileInputStream(file)
val buffer = ByteArray(1024)
var len: Int
while (fileInputStream.read(buffer).also { len = it } != -1) {
print(String(buffer, 0, len))
}
fileInputStream.close()
println(“-------------”)

//使用扩展方法
fileInputStream = FileInputStream(file)
print(String(fileInputStream.readBytes()))
fileInputStream.close()

使用String的扩展方法toByteArray(),将String转换为字节数组,然后写入到文件。读取时,按块读取,设置块的大小为1024,每读取一块就打印一块。也可以使用InputStreamd类的扩展方法readBytes()一次性读出所有的内容,但是这种方法不适合大文件

缓冲流

为了提高数据读写的速度,JavaAPI提供了带缓冲功能的流,在使用这些流类,会创建一个内部缓冲区数组。缓冲流要“套接”在相应的其他流之上(创建时需要传入其他流),对读写的数据提供了缓冲的功能,提高了读写的效率。写出的数据会先在内存中缓存,使用flush()将会使内存中的数据立刻写出。字符流的缓冲流类为BufferedReader和BufferedWriter,字节流的缓冲流类为BufferedInputStream和BufferedOutputStream。

BufferedReader、BufferedWriter

BufferedReader需要套接在其他的Reader上使用,所以创建时需要传入一个Reader,例如传入一个FileReader,BufferedWriter也是如此。也可以通过Reader和Writer类的扩展函数buffered()返回一个创建好的BufferedReader或BufferedWriter。BufferedReader继承自Reader,所以Reader类的readLines()、forEachLine、useLines扩展函数,BufferedReader同样也可以使用。BufferedReader、BufferedWriter使用示例如下:

var fileWriter = FileWriter(file, true)
val bufferedWriter = BufferedWriter(fileWriter)
bufferedWriter.write(“新增一行\n”)
bufferedWriter.close()

//使用扩展函数buffered获取BufferedWriter,
// 然后用BufferedWriter的扩展函数appendLine添加一行
fileWriter = FileWriter(file, true)
fileWriter.buffered().apply {
appendLine(“扩展函数新增一行”)
close()
}

var fileReader = FileReader(file)
val bufferedReader = BufferedReader(fileReader)
var buffer: CharArray = CharArray(1024)
var len: Int
while (bufferedReader.read(buffer).also { len = it } != -1) {
print(String(buffer, 0, len))
}
bufferedReader.close()
println(“-------------------”)

//使用扩展函数buffered获取BufferedReader,
// 然后用BufferedReader的扩展函数forEachLine逐行获取
fileReader = FileReader(file)
fileReader.buffered().forEachLine {
println(it)
}
println(“-------------------”)

fileReader = FileReader(file)
val lines = fileReader.buffered().readLines()

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

Tw3Zwy3-1710927238294)]
[外链图片转存中…(img-eGrJ1wus-1710927238295)]
[外链图片转存中…(img-ECYSqfln-1710927238295)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-ippqb47H-1710927238296)]

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐