應用場景:
跨進程傳輸大數據,如文件、圖片等;
技術選型:
共享內存–MemoryFile;
優點:
1. 共享內存沒有傳輸大小限制,所以和應用總的分配內存一樣(512MB);
2. MemoryFile 是對 SharedMemory 的包裝,使用簡單便于管理;
實現步驟:
(以A進程共享文件a.txt給B進程為例)
A進程: 創建共享內存空間工具類
?
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();}}}
A進程:創建aidl接口,使用binder接口傳遞文件描述符
interface IMemoryFileApi {ParcelFileDescriptor getParcelFileDescriptor(String type, String params);boolean setParcelFileDescriptor(String type, in ParcelFileDescriptor pfd, String params);oneway void releaseParcelFileDescriptor(String type);
}
B進程:通過bindService連接到A進程,并調用aidl接口獲取文件描述符
/*** 通過 binder 接口獲取遠程進程共享內存的文件描述符*/private ParcelFileDescriptor getParcelFileDescriptor() {try {if (iMemoryFileApi != null) {ParcelFileDescriptor pfd = iMemoryFileApi.getParcelFileDescriptor();return pfd;}} catch (Exception e) {e.printStackTrace();}return null;}
B進程:通過文件描述符讀取數據流即可;
注意:
文件描述符在每個進程都有副本,A進程的文件描述符被B進程接收后,實際上已經有了兩份文件描述符,即兩個進程有各自的內存映射空間。所以B進程讀取數據流之后,除了要關閉自己進程的文件描述符對象之外,還要調用接口關閉A進程中的文件描述符;
B進程想要把修改后的文件數據回寫給A進程時,需要做的操作和A進程的操作是完全一樣的,把文件數據重新創建共享內存,再把文件描述符通過binder接口傳遞給A進程即可;
總結
網上很多時間比較久的貼子,通過各種反射在A進程獲取MemoryFIle來讀取共享數據,這種方式并不可取;MemoryFile新版本的封裝方式就體現了它的使用方式,Google是希望隨時使用隨時創建MemoryFile并把文件描述附共享出去這種方式來實現功能的。
android MemoryFile內存共享
進程之間傳遞數據,由于Binder傳遞數據有限制1M,所以如果遇到大的數據傳遞的時候就需要使用使用到MemoryFile內存共享來解決,最合適不過了
首先,MemoryFile是基于Binder自帶的transact方法進行傳輸數據的,因此直接繼承Binder即可,不過一般項目中免不了傳遞一些基本數據類型或者bean數據,因此一般結合aidl一起使用。
android aidl使用記錄
- 服務端處理數據
private byte[] buffer = new byte[1024];//public class MyBinder extends IRtcService.Stub {public class MyBinder extends Binder {//此方法Binder自帶@Overridepublic 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);}}
- 客戶端
private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic 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();}}@Overridepublic void onServiceDisconnected(ComponentName name) {if (mConnection != null) {try {iRtcService = null;unbindService(mConnection);} catch (Exception e) {}}}};
-
結果
客戶端
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
?服務端
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內存映射文件實現
1. 什么是內存映射文件
內存映射文件是一種將磁盤上的文件映射到內存中的方法。通過內存映射文件,可以將文件的內容直接映射到內存中的一個地址空間,從而可以直接對內存進行讀寫操作,而無需通過傳統的文件IO操作。
在Android開發中,內存映射文件常常用于處理大文件或者需要頻繁讀寫的文件,因為通過內存映射文件可以獲得更高的IO性能。
2. Android內存映射文件的實現方式
Android提供了MemoryFile類來實現內存映射文件的功能。MemoryFile是一個基于共享內存的IPC(進程間通信)機制,它允許一個進程將一個內存映射文件映射到另一個進程的地址空間中。
下面是一個簡單的代碼示例,演示了如何使用MemoryFile實現內存映射文件:
// 創建一個內存映射文件
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();
在上面的代碼中,首先我們創建了一個大小為1024字節的內存映射文件。然后,我們向內存映射文件寫入了字符串數據,接著又從內存映射文件中讀取了數據,并將其轉換為字符串。最后,我們釋放了內存映射文件。
需要注意的是,MemoryFile類只能在同一個進程的不同線程之間進行通信,如果需要在不同進程之間通信,則需要使用其他的IPC機制,比如Binder。
3. 內存映射文件的優勢和應用場景
內存映射文件相比于傳統的文件IO操作有如下優勢:
- 更高的IO性能:由于內存映射文件將文件內容映射到內存中,所以可以避免頻繁的磁盤IO操作,從而獲得更高的IO性能。
- 更低的內存占用:內存映射文件只將文件的部分或全部內容映射到內存中,而不是將整個文件加載到內存中,所以可以減少內存的占用。
- 更方便的數據訪問:通過內存映射文件,可以直接對內存中的數據進行讀寫操作,而無需通過文件IO相關的API,從而簡化了數據訪問的操作。
內存映射文件常常應用于以下場景:
- 大文件處理:當需要處理大文件時,通過內存映射文件可以獲得更高的IO性能。
- 頻繁讀寫文件:當需要頻繁讀寫文件時,通過內存映射文件可以避免頻繁的磁盤IO操作,提高程序的響應速度。
- 進程間通信:通過內存映射文件可以在同一個進程的不同線程之間進行通信。
4. 總結
本文介紹了Android內存映射文件的實現方式以及其優勢和應用場景。通過內存映射文件,我們可以獲得更高的IO性能和更方便的數據訪問方式。在處理大文件或者需要頻繁讀寫文件的場景下,使用內存映射文件可以提高程序的性能和響應速度。
?