5.4 Java访问接口层详解
分析完匿名共享内存的C++访问接口层后,在本节开始分析其Java访问接口层的实现过程。在Android应用程序框架层中,通过使用接口MemoryFile来封装匿名共享内存文件的创建和使用。接口MemoryFile在文件“frameworks/base/core/java/android/os/MemoryFile.java”中定义,具体实现代码如下所示:
public class MemoryFile { private static String TAG = "MemoryFile"; //mmap(2) protection flags from <sys/mman.h> private static final int PROT_READ = 0x1; private static final int PROT_WRITE = 0x2; private static native FileDescriptor native_open(String name, int length) throws IOException; //returns memory address for ashmem region private static native int native_mmap(FileDescriptor fd, int length, int mode) throws IOException; private static native void native_munmap(int addr, int length) throws IOException; private static native void native_close(FileDescriptor fd); private static native int native_read(FileDescriptor fd, int address, byte[] buffer, int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; private static native void native_write(FileDescriptor fd, int address, byte[] buffer, int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException; private static native int native_get_size(FileDescriptor fd) throws IOException; private FileDescriptor mFD; //ashmem file descriptor private int mAddress; //address of ashmem memory private int mLength; //total length of our ashmem region private boolean mAllowPurging = false; //true if our ashmem region is unpinned /** * Allocates a new ashmem region. The region is initially not purgable. * * @param name optional name for the file (can be null). * @param length of the memory file in bytes. * @throws IOException if the memory file could not be created. */ public MemoryFile(String name, int length) throws IOException { mLength = length; mFD = native_open(name, length); if (length > 0) { mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE); } else { mAddress = 0; } }
在上述代码中,构造方法MemoryFile以指定的字符串调用了JNI方法native_open,目的是建立一个匿名共享内存文件,这样可以得到一个文件描述符。然后使用这个文件描述符为参数调用JNI方法natvie_mmap,并把匿名共享内存文件映射到进程空间中,这样就可以通过映射得到地址空间的方式直接访问内存数据。
再看JNI函数android_os_MemoryFile_get_size,此函数在文件“frameworks\base\core\jni\android_os_MemoryFile.cpp”中定义,具体实现代码如下所示:
static jint android_os_MemoryFile_get_size(JNIEnv* env, jobject clazz, jobject fileDescriptor) { int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); //Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region. //ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel //should return ENOTTY for all other valid file descriptors int result = ashmem_get_size_region(fd); if (result < 0) { if (errno == ENOTTY) { //ENOTTY means that the ioctl does not apply to this object, //i.e., it is not an ashmem region. return (jint) -1; } //Some other error, throw exception jniThrowIOException(env, errno); return (jint) -1; } return (jint) result; }
再看JNI函数android_os_MemoryFile_open,此函数在文件“frameworks\base\core\jni\android_os_MemoryFile.cpp”中定义,具体实现代码如下所示:
static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length) { const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL); int result = ashmem_create_region(namestr, length); if (name) env->ReleaseStringUTFChars(name, namestr); if (result < 0) { jniThrowException(env, "java/io/IOException", "ashmem_create_region failed"); return NULL; } return jniCreateFileDescriptor(env, result); }
再看JNI函数android_os_MemoryFile_mmap,此函数在文件“frameworks\base\core\jni\android_os_MemoryFile.cpp”中定义,具体实现代码如下所示:
static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor, jint length, jint prot) { int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0); if (! result) jniThrowException(env, "java/io/IOException", "mmap failed"); return result; }
在文件“frameworks/base/core/java/android/os/MemoryFile.java”中,再看类MemoryFile的成员函数readBytes,功能是读取某一块匿名共享内存的内容。具体实现代码如下所示:
public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count) throws IOException { if (isDeactivated()) { throw new IOException("Can't read from deactivated memory file."); } if (destOffset < 0 || destOffset > buffer.length || count < 0 || count > buffer.length - destOffset || srcOffset < 0 || srcOffset > mLength || count > mLength - srcOffset) { throw new IndexOutOfBoundsException(); } return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging); }
在文件“frameworks/base/core/java/android/os/MemoryFile.java”中,再看类MemoryFile的成员函数writeBytes,功能是写入某一块匿名共享内存的内容。具体实现代码如下所示:
public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count) throws IOException { if (isDeactivated()) { throw new IOException("Can't write to deactivated memory file."); } if (srcOffset < 0 || srcOffset > buffer.length || count < 0 || count > buffer.length - srcOffset || destOffset < 0 || destOffset > mLength || count > mLength - destOffset) { throw new IndexOutOfBoundsException(); } native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging); }
在文件“frameworks/base/core/java/android/os/MemoryFile.java”中,再看类MemoryFile的成员函数isDeactivated,功能是保证匿名共享内存已经被映射到进程的地址空间中。具体实现代码如下所示:
void deactivate() { if (! isDeactivated()) { try { native_munmap(mAddress, mLength); mAddress = 0; } catch (IOException ex) { Log.e(TAG, ex.toString()); } } } private boolean isDeactivated() { return mAddress == 0; }
JNI函数native_read和native_write分别由位于C++层的函数android_os_MemoryFile_read和android_os_MemoryFile_write实现,这两个C++的函数在文件frameworks\base\core\jni\android_os_MemoryFile.cpp中定义,具体实现代码如下所示:
static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz, jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, jint count, jboolean unpinned) { int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { ashmem_unpin_region(fd, 0, 0); jniThrowException(env, "java/io/IOException", "ashmem region was purged"); return -1; } env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset); if (unpinned) { ashmem_unpin_region(fd, 0, 0); } return count; } static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz, jobject fileDescriptor, jint address, jbyteArray buffer, jint srcOffset, jint destOffset, jint count, jboolean unpinned) { int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) { ashmem_unpin_region(fd, 0, 0); jniThrowException(env, "java/io/IOException", "ashmem region was purged"); return -1; } env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset); if (unpinned) { ashmem_unpin_region(fd, 0, 0); } return count; }