深入理解Android的file.exists()

之前项目中遇到了一个问题,我希望能够查看某一个文件是否在某个目录下,但是这个目录下文件较多,想到了直接调用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
  • gingerbread(2.3)
  • ics-mr1(ice cream sandwich)

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的方法查找文件更快,后面再写。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容