我们有两个进程通过共享内存进行交互以读/写数据。一个过程是应用程序(客户端),其他过程是后台服务,在这个例子中是单独的应用程序,它是ashmem生产者服务。
AshmemService: - 这是后台运行服务,它是粘性的,这提供了使用android MemoryFile API创建共享内存通道的API。此服务创建共享内存并获取文件描述符,并通过绑定器接口将fd传递给客户端进程。
AshmemClient: - 这是从AshmemService获取共享内存通道文件描述符的应用程序。这个客户端应用程序绑定到服务,一旦连接它调用aidl接口api获取fd。 通道创建工作正常,创建共享内存。现在,为了测试目的,我正在从客户端写入字节块到共享内存。一旦完成写作,我就会尝试将相同的块用于从app收到的有效数据。
每当我尝试写入128字节的块时,从客户端应用程序,写入返回0,所以看起来我无法写入通过parcelable binder接收的FD。即使我已经验证了fd通过fd.valid()API有效。
pfd.getStatSize()返回-1。我想我没有从parcelable文件描述符中获得正确的fd。
http://developer.android.com/reference/android/os/Parcel.html说
返回的文件描述符是原始文件描述符的副本:对象和fd不同,但是在相同的底层文件流上操作,具有相同的位置等。 在反向情况下,如果我们尝试写字节块然后在同一个块中创建共享内存,那么我们可以在已经验证的应用程序中读取并且它正在按预期工作。
package com.example.service.ashmem;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.util.Log;
public class ConnectionWriter implements Parcelable {
public final static String TAG = "ConnectionWriter";
protected FileOutputStream fos = null;
protected FileDescriptor fd = null;
protected int noofBlocks = 0;
protected int blocksize = 0;
protected int lastPosition;
protected int chanId;
public ConnectionWriter(MemoryFileChannel channel, int chanId) {
this.fd = channel.getFileDescriptor();
channel.getParcelFileDescriptor();
this.chanId = chanId;
this.blocksize = channel.getBlocksize();
this.noofBlocks = channel.getNoofBlocks();
Log.d(TAG, "ctor: chanId: " + chanId + " fd valid: " + fd.valid()+ " fd: "+fd);
}
public ConnectionWriter(Parcel in) {
readFromParcel(in);
}
public static final Parcelable.Creator<ConnectionWriter> CREATOR = new
Parcelable.Creator<ConnectionWriter>() {
public ConnectionWriter createFromParcel(Parcel in) {
Log.d(TAG, "new ConnectionWriter");
return new ConnectionWriter(in);
}
@Override
public ConnectionWriter[] newArray(int arg0) {
return null;
}
};
public boolean writeBlock(ByteBuffer data, int pos)
throws IllegalArgumentException, IOException {
FileChannel fc = fos.getChannel();
Log.d(TAG, "ByteBuffer capacity : "+data.capacity());
Log.d(TAG, "location to write : "+pos*blocksize);
Log.d(TAG, "FileChannel : "+fc.isOpen() + fc.position() + fc.size());
int len = fc.write(data);
Log.d(TAG, "block write in ashmem : "+len + " fd: "+fd);
return true;
}
public void writeToParcel(Parcel out, int arg1) {
Log.d(TAG, "writeToParcel "+ "fd: "+fd + " fd valid"+ fd.valid());
out.writeInt(blocksize);
out.writeInt(noofBlocks);
out.writeFileDescriptor(fd);
out.writeInt(lastPosition);
out.writeInt(chanId);
}
public void readFromParcel(Parcel in) {
// Make sure to validate data since these are coming from an external process
blocksize = in.readInt();
noofBlocks = in.readInt();
ParcelFileDescriptor pfd = in.readFileDescriptor();
if (pfd != null) {
fd = pfd.getFileDescriptor();
Log.d(TAG, "readFromParcel "+ "fd: "+fd);
fos = new FileOutputStream(fd);
Log.v(TAG, " fd is " + fd.valid());
long stat = pfd.getStatSize();
Log.d(TAG, " fd stat " + stat);
}
lastPosition = in.readInt();
chanId = in.readInt();
}
public int getId() {
return chanId;
}
@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
public void close() {
try {
if (fos != null)
fos.close();
} catch (IOException ioe) {
}
}
}
以下是adb logcat上的日志
ConnectionWriter(31694): new ConnectionWriter
ConnectionWriter(31694): readFromParcel fd: FileDescriptor[58]
ConnectionWriter(31694): fd is true
ConnectionWriter(31694): fd stat -1
ConnectionWriter(31694): ByteBuffer capacity : 128
ConnectionWriter(31694): location to write : 0
ConnectionWriter(31694): FileChannel : true00
ConnectionWriter(31694): block write in ashmem : 0 fd: FileDescriptor[58]
我不确定,但看起来我没有得到正确的ParcelFileDescriptor。如果有人有任何关于共享MemoryFile fd的建议。请建议。