Android MemoryFile shared memory

Application scenarios:

Transfer big data across processes, such as files, pictures, etc.;

Technology selection:

Shared memory – MemoryFile;

advantage:

1. There is no transfer size limit for shared memory, so it is the same as the total allocated memory of the application (512MB);
2. MemoryFile is a wrapper for SharedMemory, which is simple to use and easy to manage; < /span>

Implementation steps:

(Take process A sharing file a.txt to process B as an example)

Process A: Create shared memory space tool class
 
public class ShareMemoryUtils {

    private static ParcelFileDescriptor getPfdFromMemoryFile(final String name, final byte[] bytes) {
        ParcelFileDescriptor pfd = null;
        try {
                    long startTime = System.currentTimeMillis();
                    MemoryFile memoryFile = null;
                    try {
                        memoryFile = new MemoryFile(name, bytes.length);
                        memoryFile.allowPurging(true);
                        memoryFile.writeBytes(bytes, 0, 0, bytes.length);
                        pfd = getParcelFileDescriptor(memoryFile);
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        closeMemoryFile(memoryFile, null);
                    }
                }
            });
        }
        return pfd;
    }

    private static ParcelFileDescriptor getParcelFileDescriptor(MemoryFile memoryFile) {
        try {
            Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
            method.setAccessible(true);
            FileDescriptor fd = (FileDescriptor) method.invoke(memoryFile);
            return ParcelFileDescriptor.dup(fd);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static void closeMemoryFile(MemoryFile memoryFile, ParcelFileDescriptor pfd) {
        if (pfd != null) {
            try {
                pfd.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (memoryFile != null) {
            memoryFile.close();
        }
    }

}
Process A: Create the aidl interface and use the binder interface to pass the file descriptor
interface IMemoryFileApi {
    ParcelFileDescriptor getParcelFileDescriptor(String type, String params);
    boolean setParcelFileDescriptor(String type, in ParcelFileDescriptor pfd, String params);
    oneway void releaseParcelFileDescriptor(String type);
}
Process B: Connect to process A through bindService and call the aidl interface to obtain the file descriptor
/**
     * 通过 binder 接口获取远程进程共享内存的文件描述符
     */
    private ParcelFileDescriptor getParcelFileDescriptor() {
        try {
            if (iMemoryFileApi != null) {
                ParcelFileDescriptor pfd = iMemoryFileApi.getParcelFileDescriptor();
                return pfd;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
Process B: Just read the data stream through the file descriptor;

Notice:

The file descriptor has a copy in each process. After the file descriptor of process A is received by process B, there are actually two file descriptors, that is, the two processes have their own memory mapping space. Therefore, after process B reads the data stream, in addition to closing the file descriptor object of its own process, it also needs to call the interface to close the file descriptor in process A;
Process B wants to modify the When the final file data is written back to process A, the operations that need to be done are exactly the same as those of process A. Recreate the file data into shared memory, and then pass the file descriptor to process A through the binder interface;

Summarize

Many older posts on the Internet use various reflections to obtain MemoryFIle in process A to read shared data. This method is not advisable; the encapsulation method of the new version of MemoryFile reflects its usage. Google hopes to use it at any time. This function is implemented by creating a MemoryFile at any time and attaching and sharing the file description.


android MemoryFile memory sharing

To transfer data between processes, since Binder has a limit of 1M in transferring data, if you encounter large data transfer, you need to use MemoryFile memory sharing to solve the problem, which is most suitable.

First of all, MemoryFile transmits data based on Binder's own transact method, so it can be directly inherited from Binder. However, in general projects, it is inevitable to pass some basic data types or bean data, so it is generally used in conjunction with aidl.

android aidl usage record

  • Server-side processing data

    private byte[] buffer = new byte[1024];
	//public class MyBinder extends IRtcService.Stub {
    public class MyBinder extends Binder {
		//此方法Binder自带
        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            LogUtil.e("接收到远端调用" + "code" + code);
            if (code == 100) {
                try {
                    ParcelFileDescriptor pfd = data.readParcelable(null);
                    // 或者
//                    ParcelFileDescriptor pfd = data.readFileDescriptor();
                    FileDescriptor fileDescriptor = pfd.getFileDescriptor();
                    FileInputStream fi = new FileInputStream(fileDescriptor);
                    fi.read(buffer);
                    fi.close();
                    LogUtil.e("--->" + new String(buffer).replace("\0", ""));

                    //返回给客户端
                    reply.writeString("服务器接受数据成功");
                    reply.writeInt(200);
                    return true;
                } catch (IOException e) {
                    e.printStackTrace();
                }


            }
            return super.onTransact(code, data, reply, flags);
        }

    }
  • client

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
        try {
                /**
                 *
                 */
                // 参数1文件名,可为null,参数2文件长度
                mMemoryFile = new MemoryFile(null, 1024);
                //在设置了allowPurging为false之后,这个MemoryFile对应的Ashmem就会被标记成"pin",
                // 那么即使在android系统内存不足的时候,也不会对这段内存进行回收
                mMemoryFile.allowPurging(false);
                android.os.Parcel data = android.os.Parcel.obtain();
                android.os.Parcel reply = android.os.Parcel.obtain();

                byte[] buffer = "31283216382163812362183621832163812".getBytes();
                mMemoryFile.writeBytes(buffer, 0, 0, buffer.length);
                Method getFileDescriptorMethod = mMemoryFile.getClass().getDeclaredMethod("getFileDescriptor");
                if (getFileDescriptorMethod != null) {
                    FileDescriptor fileDescriptor = (FileDescriptor) getFileDescriptorMethod.invoke(mMemoryFile);
                    // 序列化,才可传送
                    ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(fileDescriptor);

                    //写入数据,对应服务端用data.readParcelable(null)接收数据
                    data.writeParcelable(pfd, 0);
                    // 或者,对应服务端用data.readFileDescriptor()接收数据
//                    data.writeFileDescriptor(fileDescriptor);
                    /**
                     * code 是一个整形的唯一标识,用于区分执行哪个方法,客户端会传递此参数,告诉服务端执行哪个方法;
                     * data客户端传递过来的参数;
                     * replay服务器返回回去的值;
                     * flags标明是否有返回值,0为有(双向),1为没有(单向)。
                     */
                    service.transact(100, data, reply, 0);

                    //服务器返回的值
                    String message = reply.readString();
                    LogUtil.e("--message--->" + message);
                    int code = reply.readInt();
                    LogUtil.e("--code--->" + code);
                    if (code==200){
                    	data.recycle();
                        reply.recycle();
                        mMemoryFile.close();
                        mMemoryFile=null;
                    }

                }


            } catch (RemoteException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (mConnection != null) {
                try {
                    iRtcService = null;
                    unbindService(mConnection);
                } catch (Exception e) {

                }
            }
        }
    };
  • result

    client

03-15 21:16:36.011 4327-4327/com.fuyao.elf_android_remote E/elf_remote: --message--->服务器接受数据成功
03-15 21:16:36.011 4327-4327/com.fuyao.elf_android_remote E/elf_remote: --code--->200

 Server

03-15 21:16:36.010 4300-4313/com.fuyao.elf_android_center:rtc_remote E/elf_center: 接收到远端调用code100
03-15 21:16:36.010 4300-4313/com.fuyao.elf_android_center:rtc_remote E/elf_center: --->31283216382163812362183621832163812

Android memory mapping file implementation

1. What is a memory mapped file?

Memory mapped files are a method of mapping files on disk into memory. Through memory-mapped files, the contents of the file can be directly mapped to an address space in the memory, so that the memory can be read and written directly without going through traditional file IO operations.

In Android development, memory mapped files are often used to process large files or files that require frequent reading and writing, because higher IO performance can be obtained through memory mapped files.

2. How to implement Android memory mapped files

Android provides the MemoryFile class to implement the function of memory mapped files. MemoryFile is a shared memory-based IPC (inter-process communication) mechanism that allows one process to map a memory-mapped file into the address space of another process.

Here is a simple code example that demonstrates how to use MemoryFile to implement a memory mapped file:

// 创建一个内存映射文件
MemoryFile memoryFile = new MemoryFile("test", 1024);

// 向内存映射文件写入数据
String data = "Hello, MemoryFile!";
byte[] buffer = data.getBytes();
memoryFile.writeBytes(buffer, 0, 0, buffer.length);

// 从内存映射文件读取数据
byte[] readBuffer = new byte[buffer.length];
memoryFile.readBytes(readBuffer, 0, 0, readBuffer.length);
String readData = new String(readBuffer);

// 打印读取的数据
System.out.println(readData);

// 释放内存映射文件
memoryFile.close();


In the above code, first we create a memory mapped file with a size of 1024 bytes. We then wrote the string data to the memory mapped file, then read the data from the memory mapped file and converted it to a string. Finally, we free the memory mapped file.

It should be noted that the MemoryFile class can only communicate between different threads of the same process. If you need to communicate between different processes, you need to use other IPC mechanisms, such as Binder.

3. Advantages and application scenarios of memory mapped files


Memory mapped files have the following advantages compared to traditional file IO operations:

  • Higher IO performance: Since memory-mapped files map file contents into memory, frequent disk IO operations can be avoided, resulting in higher IO performance.
  • Lower memory usage: Memory-mapped files only map part or all of the file into memory, rather than loading the entire file into memory, so they can reduce memory usage.
  • More convenient data access: Through memory-mapped files, data in memory can be read and written directly without going through file IO-related APIs, thus simplifying data access operations.

Memory mapped files are often used in the following scenarios:

  • Large file processing: When large files need to be processed, higher IO performance can be obtained through memory mapped files.
  • Frequent reading and writing of files: When frequent reading and writing of files is required, memory mapping files can avoid frequent disk IO operations and improve the response speed of the program.
  • Inter-process communication: Communication between different threads of the same process can be carried out through memory mapped files.

4. Summary

This article introduces the implementation of Android memory mapping files, as well as its advantages and application scenarios. Through memory mapped files, we can obtain higher IO performance and more convenient data access. In scenarios where large files are processed or files need to be read and written frequently, using memory mapped files can improve program performance and response speed.
 

Guess you like

Origin blog.csdn.net/jdsjlzx/article/details/134587904