Android下的文件读写

本文详细解析了Java中文件读写的基本原理,包括FileInputStream/FileOutputStream及RandomAccessFile的使用方式与内部实现机制,深入探讨了这些类如何通过JNI调用UNIX系统调用进行文件操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、 分析

Java中常用的文件读写的两个类:

1.FileInputStream/FileOutputStream(FileReader/FileWriter)

2.RandomFileAccess

FileInputSteamFileOutputStream继承于InputStreamOutputStreamFileReaderFileWriter继承于ReaderWriter,它们的底层实现原理其实是样的,区别在于一个前者用于字节型数据流读写,后者用于unicode文本流读写

RandomFileAccess是一个独立的文件读写类,它与InputStreamOutputStream不同之处在于它更倾向与随机文件读写,类似C语言fopenfreadfseekfwritefflushfclose的封装。

eg.FileOutputStream

File file2 = new File("FileOut.txt");
if(file2 == null)
{
            dbgOutput("ERR","File 2 Can't make");
            return;
}
FileOutputStream fos = new FileOutputStream(file2); //此处才会创建文件出来
if(fos == null)
{                                 
        dbgOutput("ERR","File 2 Output stream can't make");
        return;
}
byte [] words = {'a','b','c','d','e'};
fos.write(words);
fos.flush();
fos.close();
eg.RandomAccessFile
RandomAccessFile rf = new RandomAccessFile("rtest.dat", "rw"); 
        for (int i = 0; i < 10; i++) { 
            //写入基本类型double数据 
            rf.writeDouble(i * 1.414); 
        } 
        rf.close(); 
        rf = new RandomAccessFile("rtest.dat", "rw"); 
        //直接将文件指针移到第5个double数据后面 
        rf.seek(5 * 8); 
        //覆盖第6个double数据 
        rf.writeDouble(47.0001); 
        rf.close(); 
        rf = new RandomAccessFile("rtest.dat", "r"); 
        for (int i = 0; i < 10; i++) { 
            System.out.println("Value " + i + ": " + rf.readDouble()); 
        } 
        rf.close();

2 、实现:


Android Java 文件读写IO类的具体实现的代码在libcore中实现,原理也是通过JNI的方式实现的,对于

FileInputStreamFileOutStream类,继承于InputStreamOutputStream类,但InputStreamOutputStream只声明了抽象的readwrite接口,如:

@ \libcore\luni\src\main\java\java\io\InputStream.java

@ \libcore\luni\src\main\java\java\io\OutputStream.java

public abstract int read() throws IOException;
public abstract void write(int oneByte) throws IOException;

具体的实现还是在FileInputStreamFileOutputStream中重写的,其中实现的代码如下:

@ \libcore\luni\src\main\java\java\io\FileInputStream.java

@ \libcore\luni\src\main\java\java\io\FileOutputStream.java

    @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
        return IoBridge.read(fd, buffer, byteOffset, byteCount);
    }
    public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
        IoBridge.write(fd, buffer, byteOffset, byteCount);
    }

其中调用了IoBridge类实现,readwrite方法都是静态方法,实现的代码如下:

@ \android\libcore\luni\src\main\java\libcore\io\IoBridge.java

read方法

 /**
     * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional
     * Unix practice where you'd read until you got 0 bytes (and any future read would return -1).
     */
    public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
        if (byteCount == 0) {
            return 0;
        }
        try {
            int readCount = Libcore.os.read(fd, bytes, byteOffset, byteCount);
            if (readCount == 0) {
                return -1;
            }
            return readCount;
        } catch (ErrnoException errnoException) {
            if (errnoException.errno == EAGAIN) {
                // We return 0 rather than throw if we try to read from an empty non-blocking pipe.
                return 0;
            }
            throw errnoException.rethrowAsIOException();
        }
    }

@ \android\libcore\luni\src\main\java\libcore\io\IoBridge.java

write方法:

/**
     * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike
     * Unix it never just writes as many bytes as happens to be convenient.)
     */
    public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
        Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
        if (byteCount == 0) {
            return;
        }
        try {
            while (byteCount > 0) {
                int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount);
                byteCount -= bytesWritten;
                byteOffset += bytesWritten;
            }
        } catch (ErrnoException errnoException) {
            throw errnoException.rethrowAsIOException();
        }
    }

LibCore类只有Os这一个对象:

\libcore\luni\src\main\java\libcore\io\LibCore.java

package libcore.io;
 
public final class Libcore {
    private Libcore() { }
 
    public static Os os = new BlockGuardOs(new Posix());
}

Os对象是一系列系统调用的抽象接口,从LibCore.java中可以看出它是通过Posix这个类实现的,这个类中读写的实现如下:

@\libcore\luni\src\main\java\libcore\io\Posix.java

public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
        if (buffer.isDirect()) {
            return readBytes(fd, buffer, buffer.position(), buffer.remaining());
        } else {
            return readBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining());
        }
    }
    public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
        // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
        return readBytes(fd, bytes, byteOffset, byteCount);
    }
    private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
  public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
        if (buffer.isDirect()) {
            return writeBytes(fd, buffer, buffer.position(), buffer.remaining());
        } else {
            return writeBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining());
        }
    }
    public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
        // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
        return writeBytes(fd, bytes, byteOffset, byteCount);
    }
    private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;

最终是通过native调用来实现的,

@\libcore\luni\src\main\native\libcore_io_Posix.cpp

static jint Posix_writeBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount) {
    ScopedBytesRO bytes(env, javaBytes);
    if (bytes.get() == NULL) {
        return -1;
    }
    int fd = jniGetFDFromFileDescriptor(env, javaFd);
    return throwIfMinusOne(env, "write", TEMP_FAILURE_RETRY(write(fd, bytes.get() + byteOffset, byteCount)));
}
static jint Posix_readBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount) {
    ScopedBytesRW bytes(env, javaBytes);
    if (bytes.get() == NULL) {
        return -1;
    }
    int fd = jniGetFDFromFileDescriptor(env, javaFd);
    return throwIfMinusOne(env, "read", TEMP_FAILURE_RETRY(read(fd, bytes.get() + byteOffset, byteCount)));
}

对于RandomAccessFile的调用过程:

@\libcore\luni\src\main\java\java\io\RandomAccessFile.java

public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
        return IoBridge.read(fd, buffer, byteOffset, byteCount);
    }
    public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
        IoBridge.write(fd, buffer, byteOffset, byteCount);
        // if we are in "rws" mode, attempt to sync file+metadata
        if (syncMetadata) {
            fd.sync();
        }
    }

虽然RandomAccessFilejava中是与FileInputStreamFileOutputStream设计得完全不同的类,但其底层实现还是一样的。

 3、总结

至此,Java的文件读写调用最终转换成UNIX系统调用,并且Android java中的flush函数是个空的函数,从整个过程来看,FileInputStream/FileOutputStreamRandomAccessFile并没有使用任何的缓冲机制,且其调用过程也比较简单明了。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值