深入理解Android的file.exists()

2018-11-12  本文已影响0人  沙夏cc

之前项目中遇到了一个问题,我希望能够查看某一个文件是否在某个目录下,但是这个目录下文件较多,想到了直接调用file.exists()去判断,又觉得如果文件特别多,这样io的操作效率是不是很低?就自己找事,写了一个保存所有文件名的文件,按行保存,然后去查的时候,遍历一下该文件,按行去对比文件名称。

结果当然是我自己写的遍历文件的要慢的多,去源码研究下这是为什么。

/**
     * Returns a boolean indicating whether this file can be found on the
     * underlying file system.
     *
     * @return {@code true} if this file exists, {@code false} otherwise.
     */
    public boolean exists() {
        return doAccess(F_OK);
    }
    

再点进去

private boolean doAccess(int mode) {
        try {
            return Libcore.os.access(path, mode);
        } catch (ErrnoException errnoException) {
            return false;
        }
    }

我的Android Studio是链接不到Libcore这个类的,没关系,google搜索一下。libcore.io.Libcore。
https://android.googlesource.com/platform/libcore/ 但是google的git上libcore的版本太多了,咱们尽量找一个简单的看。

\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变量。
涉及到两个类,一个BlockGuardOs,一个Posix。继续搜索,能够搜到一些android从java到c层读取文件的流程的说明,也可以看看。这里暂时不涉及。
看下BlockGuardOs这个类。

/**
 * Informs BlockGuard of any activity it should be aware of.
 */
public class BlockGuardOs extends ForwardingOs {
    public BlockGuardOs(Os os) {
        super(os);
    }
    
    @Override public boolean access(String path, int mode) throws ErrnoException {
        BlockGuard.getThreadPolicy().onReadFromDisk();
        return os.access(path, mode);
    }
......

这个类里面的方法都是上面的样子。os.access方法使我们要找的。BlockGuard是什么呢?搜索的时候我们发现
https://android.googlesource.com/platform/libcore-snapshot/
上是有很多个版本的代码,分别是

master上一堆注解,不好理解,我选择ics-mr1这个分支的代码。

public final class BlockGuard {

    public interface Policy {
        /**
         * Called on disk writes.
         */
        void onWriteToDisk();
        /**
         * Called on disk reads.
         */
        void onReadFromDisk();
        /**
         * Called on network operations.
         */
        void onNetwork();
        /**
         * Returns the policy bitmask, for shipping over Binder calls
         * to remote threads/processes and reinstantiating the policy
         * there.  The bits in the mask are from the DISALLOW_* and
         * PENALTY_* constants.
         */
        int getPolicyMask();
    }
......

所以调用的其实是这个接口的方法。

再找下Os类:https://android.googlesource.com/platform/libcore/+/android-6.0.1_r21/luni/src/main/java/android/system/Os.java
\libcore\luni\src\main\java\libcore\io\Os.java

public final class Os {
  private Os() {}
  /**
   * See <a href="http://man7.org/linux/man-pages/man2/access.2.html">access(2)</a>.
   */
  public static boolean access(String path, int mode) throws ErrnoException { return Libcore.os.access(path, mode); }

......

所以Os对象是一系列系统调用的抽象接口,再看下刚才的Posix类
\libcore\luni\src\main\java\libcore\io\Posix.java

public final class Posix implements Os {
    Posix() { }
    public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException;
    public native boolean access(String path, int mode) throws ErrnoException;

......

这就找到native方法的接口了。到这里就不好搜索了,只能看下别人的介绍,能够了解到Posix.java通过JNI (libcore_io_Posix.cpp)调用linux的posix API,那么知道了,C层的实现都在libcore_io_Posix.cpp中,咱们来看下。
\libcore\luni\src\main\native\libcore_io_Posix.cpp

......

static jboolean Posix_access(JNIEnv* env, jobject, jstring javaPath, jint mode) {
    ScopedUtfChars path(env, javaPath);
    if (path.c_str() == NULL) {
        return JNI_FALSE;
    }
    int rc = TEMP_FAILURE_RETRY(access(path.c_str(), mode));
    if (rc == -1) {
        throwErrnoException(env, "access");
    }
    return (rc == 0);
}

......

static JNINativeMethod gMethods[] = {
    NATIVE_METHOD(Posix, accept, "(Ljava/io/FileDescriptor;Ljava/net/InetSocketAddress;)Ljava/io/FileDescriptor;"),
    NATIVE_METHOD(Posix, access, "(Ljava/lang/String;I)Z"),
    NATIVE_METHOD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
    NATIVE_METHOD(Posix, chmod, "(Ljava/lang/String;I)V"),
    
    ......
};

int register_libcore_io_Posix(JNIEnv* env) {
    return jniRegisterNativeMethods(env, "libcore/io/Posix", gMethods, NELEM(gMethods));
}

到这个地方,就看到Android是如何从java层,调用到linux层的了,虽然这里只写了access,但其实可以引申至非常多io的方法。

当然这并没有解释为什么native的方法查找文件更快,后面再写。

上一篇 下一篇

猜你喜欢

热点阅读