03
2017
03

FileDescriptor到底实现了那些功能

之前已经提到过FileDescriptor,利用FileDescriptor我可以方便的实现播放资源包中的视频文件,而不用先解压出来。

这么神奇的一个类,究竟实现了哪些功能呢?

看了看源码:

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package java.io;

import libcore.io.ErrnoException;
import libcore.io.Libcore;
import static libcore.io.OsConstants.*;

/**
 * Wraps a Unix file descriptor. It's possible to get the file descriptor used by some
 * classes (such as {@link FileInputStream}, {@link FileOutputStream},
 * and {@link RandomAccessFile}), and then create new streams that point to the same
 * file descriptor.
 */
public final class FileDescriptor {

    /**
     * Corresponds to {@code stdin}.
     */
    public static final FileDescriptor in = new FileDescriptor();

    /**
     * Corresponds to {@code stdout}.
     */
    public static final FileDescriptor out = new FileDescriptor();

    /**
     * Corresponds to {@code stderr}.
     */
    public static final FileDescriptor err = new FileDescriptor();

    /**
     * The Unix file descriptor backing this FileDescriptor.
     * A value of -1 indicates that this FileDescriptor is invalid.
     */
    private int descriptor = -1;

    static {
        in.descriptor = STDIN_FILENO;
        out.descriptor = STDOUT_FILENO;
        err.descriptor = STDERR_FILENO;
    }

    /**
     * Constructs a new invalid FileDescriptor.
     */
    public FileDescriptor() {
    }

    /**
     * Ensures that data which is buffered within the underlying implementation
     * is written out to the appropriate device before returning.
     */
    public void sync() throws SyncFailedException {
        try {
            Libcore.os.fsync(this);
        } catch (ErrnoException errnoException) {
            SyncFailedException sfe = new SyncFailedException(errnoException.getMessage());
            sfe.initCause(errnoException);
            throw sfe;
        }
    }

    /**
     * Tests whether this {@code FileDescriptor} is valid.
     */
    public boolean valid() {
        return descriptor != -1;
    }

    /**
     * Returns the int fd. It's highly unlikely you should be calling this. Please discuss
     * your needs with a libcore maintainer before using this method.
     * @hide internal use only
     */
    public final int getInt$() {
        return descriptor;
    }

    /**
     * Sets the int fd. It's highly unlikely you should be calling this. Please discuss
     * your needs with a libcore maintainer before using this method.
     * @hide internal use only
     */
    public final void setInt$(int fd) {
        this.descriptor = fd;
    }

    @Override public String toString() {
        return "FileDescriptor[" + descriptor + "]";
    }
}

有3个静态变量in,out,err,都是FileDescriptor类型的。

有个descriptor属性,int类型,私有,又实现了该属性的getter和setter,还是隐藏的,只供框架内调用。

valid方法,还是和descriptor有关,判断descriptor是不是等于-1 。

有一个sync方法,不了解,没看懂。

toString就不用多说了。


我们看,FileDescriptor其实就只有一个int类型的descriptor属性。

那么descriptor究竟是什么,又是如何实现各种文件的读写的呢?

利用之前学到的反射,我们可以读到descriptor的值(刚学的知识就用上了)。

先写一个方法利用反射来读取并输出FileDescriptor的descriptor值。代码如下:

private void shuchuDescriptor(FileDescriptor fd){
		Class<FileDescriptor> c = FileDescriptor.class;  
        Method method;
		try {
			method = c.getMethod("getInt$");
			method.setAccessible(true);  
            Object obj = method.invoke(fd, null);
            Log.d("hanyeah", "descriptor="+obj);
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

然后测试

//直接new一个FileDescriptor
shuchuDescriptor(new FileDescriptor());//输出-1
shuchuDescriptor(FileDescriptor.in);//输出0
shuchuDescriptor(FileDescriptor.out);//输出1
shuchuDescriptor(FileDescriptor.err);//输出2

for(int i=0;i<10;i++){
	FileInputStream fStreami = new FileInputStream(Environment.getExternalStorageDirectory()+"/test.mp4");
	shuchuDescriptor(fStreami.getFD());//输出50+i
}
FileInputStream fStream2 = new FileInputStream(Environment.getExternalStorageDirectory()+"/test.jpg");
shuchuDescriptor(fStream2.getFD());//输出60

descriptor只是一个id,此id是由native方法返回的,然后java代码通过调用native方法,并传入此id来控制文件流的读写。


参考FileInputStream源码,如果要深入了解文件读写是如何实现的,需要学习IoBridge,Libcore等库。太复杂了,暂时先到这里。




« 上一篇下一篇 »

相关文章:

安卓FileDescriptor  (2017-2-21 11:28:53)

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。