• MemoryFile匿名共享内存


    Android提供了一个高效的共享内存机制。如果应用中涉及到在多个进程间交换数据时使用Android提高的共享内存机制将会大大的提高效率。但是也许是出于安全考虑,在应用层使用共享内存机制将会遇到很多障碍,这篇文章旨在解决这些障碍

    frameworks/base/core/java/android/os/MemoryFile.java的源码位置下面是5.0版本的网上源码

    MemoryFile.java

    使用共享内存的流程大概是:

    在一个进程中创建一个共享内存。在Android应用层中,用MemoryFile描述一块共享内存,创建共享内存其实就是创建一MemoryFile对象。这一步非常简单,MemoryFile提供了相应的构造函数

    public MemoryFile(String name, int length) throws IOException

    因为这块内存需要让其他进程共享,所以需要取得刚刚创建的共享内存的文件描述符FileDescriptor,并且把序列化成ParcelFileDescriptor。对于获取FileDescriptor,MemoryFile提供了相应的方法

        /**
         * Gets a FileDescriptor for the memory file.
         *
         * The returned file descriptor is not duplicated.
         *
         * @throws IOException If the memory file has been closed.
         *
         * @hide
         */
        public FileDescriptor getFileDescriptor() throws IOException {
            return mFD;
        }
    @hide可见这个方法是隐藏的,在应用层没法直接调用。所以需要用反射来完成,具体代码如下
        /**
         * 获取memoryFile的FileDescriptor
         * @param memoryFile 描述一块共享内存
         * @return 这块共享内存对应的文件描述符
         */
        public static FileDescriptor getFileDescriptor(MemoryFile memoryFile){
            if(memoryFile == null){
                throw new IllegalArgumentException("memoryFile 不能为空");
            }
            FileDescriptor fd;
            fd = (FileDescriptor) ReflectUtil.invoke("android.os.MemoryFile",memoryFile,"getFileDescriptor");
            return fd;
        }

    对于把FileDescriptor序列化成ParcelFileDescriptor,ParcelFileDescriptor也提供了一隐藏的构造函数:

      /** {@hide} */
        public ParcelFileDescriptor(FileDescriptor fd) {
            this(fd, null);
        }

    所以我们还是可以通过反射来实现:

       /**
         * 获取memoryFile的ParcelFileDescriptor
         * @param memoryFile 描述一块共享内存
         * @return ParcelFileDescriptor
         */
        public static ParcelFileDescriptor getParcelFileDescriptor(MemoryFile memoryFile){
            if(memoryFile == null){
                throw new IllegalArgumentException("memoryFile 不能为空");
            }
            ParcelFileDescriptor pfd;
            FileDescriptor fd = getFileDescriptor(memoryFile);
            pfd = (ParcelFileDescriptor) ReflectUtil.getInstance("android.os.ParcelFileDescriptor",fd);
            return pfd;
        }

    把刚刚得到的ParcelFileDescriptor传递到其他进程,这个比较简单直接用binder传就可以了

    通过描述共享内存文件描述取得一个描述共享内存的MemoryFile对象,并且需要让这个MemoryFile对象指向刚刚创建的共享内存。在低版本的系统中存在一个构造函数可以直接以FileDescriptor为参数构造出一个MemoryFile对象,这样构造出来的对象刚好指向FileDescriptor描述的共享内存。但是在高版本中没有样的构造函数了。所以在这里我利用了一个取巧的方式。思路是:利用构造函数 

    public MemoryFile(String name, int length) throws IOException 

    构造一个MemoryFile对象,当然此时也创建了一块新的共享内存,但是这块共享内存不是我们需要的;调用public void close()方法关闭刚刚创建的共享内存。通过前面的操作后我们得到了一个MemoryFile对象,但是这个对象没有指向任何共享内存,所以接下来我们就需要让MemoryFile对象指向我们需要的共享内存,也就是FileDescriptor描述的那块。在MemoryFile中有一个native方法:

    private static native long native_mmap(FileDescriptor fd, int length, int mode)

    这个方法就是把fd描述的共享内存映射到虚拟地址空间中。所以我们可以已刚刚获得的FileDescriptor 作为参数利用反射调用这个方法:

    /**
         * 打开共享内存,一般是一个地方创建了一块共享内存
         * 另一个地方持有描述这块共享内存的文件描述符,调用
         * 此方法即可获得一个描述那块共享内存的MemoryFile
         * 对象
         * @param fd 文件描述
         * @param length 共享内存的大小
         * @param mode PROT_READ = 0x1只读方式打开,
         *             PROT_WRITE = 0x2可写方式打开,
         *             PROT_WRITE|PROT_READ可读可写方式打开
         * @return MemoryFile
         */
        public static MemoryFile openMemoryFile(FileDescriptor fd,int length,int mode){
            MemoryFile memoryFile = null;
            try {
                memoryFile = new MemoryFile("tem",1);
                memoryFile.close();
                Class<?> c = MemoryFile.class;
                Method native_mmap = null;
                Method[] ms = c.getDeclaredMethods();
                for(int i = 0;ms != null&&i<ms.length;i++){
                    if(ms[i].getName().equals("native_mmap")){
                        native_mmap = ms[i];
                    }
                }
                ReflectUtil.setField("android.os.MemoryFile", memoryFile, "mFD", fd);
                ReflectUtil.setField("android.os.MemoryFile",memoryFile,"mLength",length);
                long address = (long) ReflectUtil.invokeMethod( null, native_mmap, fd, length, mode);
                ReflectUtil.setField("android.os.MemoryFile", memoryFile, "mAddress", address);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return memoryFile;
        }

    这样我们就得到了一个指向一开始我们创建的那块共享内存的MemoryFile了,接下来就可以调用它的public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)从共享内存中读数据和往共享内存中写数据了

    最后上完整的代码:

    package wzr.com.slidefinish.util;
    
    import android.os.MemoryFile;
    import android.os.ParcelFileDescriptor;
    
    import java.io.FileDescriptor;
    import java.io.IOException;
    import java.lang.reflect.Method;
    
    /**
     * 对memoryFile类的扩展
     * 1.从memoryFile对象中获取FileDescriptor,ParcelFileDescriptor
     * 2.根据一个FileDescriptor和文件length实例化memoryFile对象
     */
    public class MemoryFileHelper {
        /**
         * 创建共享内存对象
          * @param name 描述共享内存文件名称
         * @param length 用于指定创建多大的共享内存对象
         * @return MemoryFile 描述共享内存对象
         */
        public static MemoryFile createMemoryFile(String name,int length){
            try {
                return new MemoryFile(name,length);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static MemoryFile openMemoryFile(ParcelFileDescriptor pfd,int length,int mode){
            if(pfd == null){
                throw new IllegalArgumentException("ParcelFileDescriptor 不能为空");
            }
            FileDescriptor fd = pfd.getFileDescriptor();
            return openMemoryFile(fd,length,mode);
        }
    
        /**
         * 打开共享内存,一般是一个地方创建了一块共享内存
         * 另一个地方持有描述这块共享内存的文件描述符,调用
         * 此方法即可获得一个描述那块共享内存的MemoryFile
         * 对象
         * @param fd 文件描述
         * @param length 共享内存的大小
         * @param mode PROT_READ = 0x1只读方式打开,
         *             PROT_WRITE = 0x2可写方式打开,
         *             PROT_WRITE|PROT_READ可读可写方式打开
         * @return MemoryFile
         */
        public static MemoryFile openMemoryFile(FileDescriptor fd,int length,int mode){
            MemoryFile memoryFile = null;
            try {
                memoryFile = new MemoryFile("tem",1);
                memoryFile.close();
                Class<?> c = MemoryFile.class;
                Method native_mmap = null;
                Method[] ms = c.getDeclaredMethods();
                for(int i = 0;ms != null&&i<ms.length;i++){
                    if(ms[i].getName().equals("native_mmap")){
                        native_mmap = ms[i];
                    }
                }
                ReflectUtil.setField("android.os.MemoryFile", memoryFile, "mFD", fd);
                ReflectUtil.setField("android.os.MemoryFile",memoryFile,"mLength",length);
                long address = (long) ReflectUtil.invokeMethod( null, native_mmap, fd, length, mode);
                ReflectUtil.setField("android.os.MemoryFile", memoryFile, "mAddress", address);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return memoryFile;
        }
    
        /**
         * 获取memoryFile的ParcelFileDescriptor
         * @param memoryFile 描述一块共享内存
         * @return ParcelFileDescriptor
         */
        public static ParcelFileDescriptor getParcelFileDescriptor(MemoryFile memoryFile){
            if(memoryFile == null){
                throw new IllegalArgumentException("memoryFile 不能为空");
            }
            ParcelFileDescriptor pfd;
            FileDescriptor fd = getFileDescriptor(memoryFile);
            pfd = (ParcelFileDescriptor) ReflectUtil.getInstance("android.os.ParcelFileDescriptor",fd);
            return pfd;
        }
    
        /**
         * 获取memoryFile的FileDescriptor
         * @param memoryFile 描述一块共享内存
         * @return 这块共享内存对应的文件描述符
         */
        public static FileDescriptor getFileDescriptor(MemoryFile memoryFile){
            if(memoryFile == null){
                throw new IllegalArgumentException("memoryFile 不能为空");
            }
            FileDescriptor fd;
            fd = (FileDescriptor) ReflectUtil.invoke("android.os.MemoryFile",memoryFile,"getFileDescriptor");
            return fd;
        }
    }

    ReflectUtil.java

    package wzr.com.slidefinish.util;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    /**
     * 反射工具类
     * Created by wuzr on 2016/6/27.
     */
    public class ReflectUtil {
    
        /**
         *根据类名,参数实例化对象
         * @param className 类的路径全名
         * @param params 构造函数需要的参数
         * @return 返回T类型的一个对象
         */
        public static Object getInstance(String className,Object ... params){
            if(className == null || className.equals("")){
                throw new IllegalArgumentException("className 不能为空");
            }
            try {
                Class<?> c = Class.forName(className);
                if(params != null){
                    int plength = params.length;
                    Class[] paramsTypes = new Class[plength];
                    for (int i = 0; i < plength; i++) {
                        paramsTypes[i] = params[i].getClass();
                    }
                    Constructor constructor = c.getDeclaredConstructor(paramsTypes);
                    constructor.setAccessible(true);
                    return constructor.newInstance(params);
                }
                Constructor constructor = c.getDeclaredConstructor();
                constructor.setAccessible(true);
                return constructor.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 执行instance的方法
         * @param className 类的全名
         * @param instance 对应的对象,为null时执行类的静态方法
         * @param methodName 方法名称
         * @param params 参数
         */
        public static Object invoke(String className,Object instance,String methodName,Object ... params){
            if(className == null || className.equals("")){
                throw new IllegalArgumentException("className 不能为空");
            }
            if(methodName == null || methodName.equals("")){
                throw new IllegalArgumentException("methodName不能为空");
            }
            try {
                Class<?> c = Class.forName(className);
                if(params != null){
                    int plength = params.length;
                    Class[] paramsTypes = new Class[plength];
                    for(int i = 0;i < plength;i++){
                        paramsTypes[i] = params[i].getClass();
                    }
                    Method method = c.getDeclaredMethod(methodName, paramsTypes);
                    method.setAccessible(true);
                    return method.invoke(instance, params);
                }
                Method method = c.getDeclaredMethod(methodName);
                method.setAccessible(true);
                return method.invoke(instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 执行指定的对方法
         * @param instance 需要执行该方法的对象,为空时,执行静态方法
         * @param m 需要执行的方法对象
         * @param params 方法对应的参数
         * @return 方法m执行的返回值
         */
        public static Object invokeMethod(Object instance,Method m,Object ... params){
            if(m == null){
                throw new IllegalArgumentException("method 不能为空");
            }
            m.setAccessible(true);
            try {
                return m.invoke(instance,params);
            } catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 取得属性值
         * @param className 类的全名
         * @param fieldName 属性名
         * @param instance 对应的对象,为null时取静态变量
         * @return 属性对应的值
         */
        public static Object getField(String className,Object instance,String fieldName){
            if(className == null || className.equals("")){
                throw new IllegalArgumentException("className 不能为空");
            }
            if(fieldName == null || fieldName.equals("")){
                throw new IllegalArgumentException("fieldName 不能为空");
            }
            try {
                Class c = Class.forName(className);
                Field field = c.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field.get(instance);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 设置属性
         * @param className 类的全名
         * @param fieldName 属性名
         * @param instance 对应的对象,为null时改变的是静态变量
         * @param value 值
         */
        public static void setField(String className,Object instance,String fieldName,Object value){
            if(className == null || className.equals("")){
                throw new IllegalArgumentException("className 不能为空");
            }
            if(fieldName == null || fieldName.equals("")){
                throw new IllegalArgumentException("fieldName 不能为空");
            }
            try {
                Class<?> c = Class.forName(className);
                Field field = c.getDeclaredField(fieldName);
                field.setAccessible(true);
                field.set(instance, value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 根据方法名,类名,参数获取方法
         * @param className 类名,全名称
         * @param methodName 方法名
         * @param paramsType 参数类型列表
         * @return 方法对象
         */
        public static Method getMethod(String className,String methodName,Class ... paramsType){
            if(className == null || className.equals("")){
                throw new IllegalArgumentException("className 不能为空");
            }
            if(methodName == null || methodName.equals("")){
                throw new IllegalArgumentException("methodName不能为空");
            }
            try {
                Class<?> c = Class.forName(className);
                return c.getDeclaredMethod(methodName,paramsType);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    源码地址: https://github.com/mingfeng002/TestMemFile

  • 相关阅读:
    mongodb复制集配置
    replication set复制集
    CentOS 6.5下源码安装MySQL 5.6
    10个最常见的 HTML5 面试题及答案
    Javascript中document.execCommand()的用法
    javascript 准确的判断类型方法
    getUserMedia API
    javascript
    C++ 面试题
    C++
  • 原文地址:https://www.cnblogs.com/mingfeng002/p/9382601.html
Copyright © 2020-2023  润新知