之前项目中遇到了一个问题,我希望能够查看某一个文件是否在某个目录下,但是这个目录下文件较多,想到了直接调用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的方法查找文件更快,后面再写。