源起
需要跟踪FileInputStream的Read的Nativie实现,开始走了弯路,Java工程下的FileInputStream实现与Android工程的实现不同。另外,http://blog.chinaunix.net/uid-26926660-id-3326678.html中分析的不错。
java.io.FileInputStream
@Override public int read() throws IOException {
return Streams.readSingleByte(this);
}
@Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
return IoBridge.read(fd, buffer, byteOffset, byteCount);
}
libcore.io.Streams
public static int readSingleByte(InputStream in) throws IOException {
byte[] buffer = new byte[1];
int result = in.read(buffer, 0, 1); // in.read() -> FileInputStream.read -> IoBridge.read
return (result != -1) ? buffer[0] & 0xff : -1;
}
最终还是调用IoBridge.read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount)
libcore.io.ioBridge
https://android.googlesource.com/platform/libcore/+/796f0d5a4e7b83c3efc5e587b6766977dc20b0c3/luni/src/main/java/libcore/io/IoBridge.java
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) { // Linux中非阻塞线程里面执行阻塞工作,会有这样的错误码,该错误码是native实现
// We return 0 rather than throw if we try to read from an empty non-blocking pipe.
return 0;
}
throw errnoException.rethrowAsIOException();
}
}
Java.util.Arrays
https://android.googlesource.com/platform/libcore/+/2bad9bff258de6275bd3847e5e9f3169b0a93c61/luni/src/main/java/java/util/Arrays.java
public static void checkOffsetAndCount(int arrayLength, int offset, int count) {
if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {
throw new ArrayIndexOutOfBoundsException(arrayLength, offset, count);
}
}
OsConstants.java
https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/libcore/io/OsConstants.java
public static final int EAGAIN = placeholder();
private static native void initConstants();
// A hack to avoid these constants being inlined by javac...
private static int placeholder() { return 0; }
// ...because we want to initialize them at runtime.
static {
initConstants();
}
libcore_io_OsConstants.cpp
https://android.googlesource.com/platform/libcore/+/2bad9bff258de6275bd3847e5e9f3169b0a93c61/luni/src/main/native/libcore_io_OsConstants.cpp
#include <errno.h> http://baike.baidu.com/view/5806465.htm
#define EAGAIN 11 /* Try again */
libcore.io.Libcore
http://grepcode.com/file/repo1.maven.org/maven2/org.robovm/robovm-rt/0.0.2/libcore/io/Libcore.java#Libcore
package libcore.io;
public final class Libcore {
private Libcore() { }
public static Os os = new BlockGuardOs(new Posix());
}
BlockGuardOs
android / platform/libcore / 2bad9bff258de6275bd3847e5e9f3169b0a93c61 / . / luni / src / main / java / libcore / io / BlockGuardOs.java
@Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk(); // BlockGuard. getThreadPolicy()是一个单例
return os.read(fd, bytes, byteOffset, byteCount); // 使用的是Posix中的read
}
Libcore.io.Posix
https://android.googlesource.com/platform/libcore/+/6c9b5377550a9649ed1532d1fcdfeba116c74ead/luni/src/main/java/libcore/io/Posix.java
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;
src/main/native/libcore_io_Posix.cpp
https://android.googlesource.com/platform/libcore/+/721ceca2a52a3c27aa751476c8562e1e68088e15/luni/src/main/native/libcore_io_Posix.cpp
#include <unistd.h>
#include "ScopeBytes.h"
static jint Posix_readBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount) {
ScopedBytesRW bytes(env, javaBytes); // ScopeBytes.h中有定义
if (bytes.get() == NULL) {
return -1;
}
int fd = jniGetFDFromFileDescriptor(env, javaFd);
return throwIfMinusOne(env, "read", TEMP_FAILURE_RETRY(read(fd, bytes.get() + byteOffset, byteCount)));
}
1. ScopedBytesRW bytes(env, javaBytes); ScopeBytes.h中有定义
https://android.googlesource.com/platform/libnativehelper/+/3d9d2148155c2e0b3bf51cd548f58f93d1199a4e/include/nativehelper/ScopedBytes.h
2. throwIfMinusOne和TEMP_FAILURE_RETRY
3. read(fd, bytes.get() + byteOffset, byteCount)就是c的库函数
throwIfMinusOne
template <typename rc_t>
static rc_t throwIfMinusOne(JNIEnv* env, const char* name, rc_t rc) {
if (rc == rc_t(-1)) {
throwErrnoException(env, name);
}
return rc;
}
static void throwErrnoException(JNIEnv* env, const char* name) {
int errnum = errno;
jthrowable cause = NULL;
if (env->ExceptionCheck()) {
cause = env->ExceptionOccurred();
env->ExceptionClear();
}
ScopedLocalRef<jstring> javaName(env, env->NewStringUTF(name));
if (javaName.get() == NULL) {
// Not really much we can do here. We're probably dead in the water,
// but let's try to stumble on...
env->ExceptionClear();
}
jobject exception;
if (cause != NULL) {
static jmethodID ctor = env->GetMethodID(JniConstants::errnoExceptionClass, "<init>",
"(Ljava/lang/String;ILjava/lang/Throwable;)V");
exception = env->NewObject(JniConstants::errnoExceptionClass, ctor,
javaName.get(), errnum, cause);
} else {
static jmethodID ctor = env->GetMethodID(JniConstants::errnoExceptionClass, "<init>",
"(Ljava/lang/String;I)V");
exception = env->NewObject(JniConstants::errnoExceptionClass, ctor, javaName.get(), errnum);
}
env->Throw(reinterpret_cast<jthrowable>(exception));
}
TEMP_FAILURE_RETRY
unistd.h,http://sinf1252.info.ucl.ac.be/usr/include/unistd.h.html
# define TEMP_FAILURE_RETRY(expression) \
(__extension__ \
({ long int __result; \
do __result = (long int) (expression); \
while (__result == -1L && errno == EINTR); \
__result; }))
#endif
本文链接
http://blog.youkuaiyun.com/xiaodongrush/article/details/10004997