匿名共享内存(Ashmem):Ashmem是一种共享内存的机制,它利用了Linux的mmap系统调用,将不同进程中的同一段物理内存映射到进程各自的虚拟地址空间,从而实现高效的进程间共享。它以驱动程序的形式实现在内核空间。它有两个特点,一是能够辅助内存管理系统来有效地管理不再使用的内存块,二是它通过Binder进程间通信机制来实现进程间的内存共享。Ashmem的两个特点就是共享和高效。共享是指可以在不同进程间共享信息,高效则是因为不同进程都是直接进行的内存操作,相对于其他的进程间通信方式来讲,这种方式会更快一些。
Android系统的匿名共享内存系统的主体是以驱动程序的形式实现在内核空间的,同时,在系统运行时库和应用程序框架层提供了访问接口,其中,在系统运行时库提供了C/C++调用接口,而在应用程序框架层提供了Java调用接口。Android开发中通常只需要调用Java接口,而实际上,应用程序框架层的Java调用接口是通过JNI方法来调用系统运行时库的C/C++调用接口,最后进入到内核空间的Ashmem驱动程序去的。
在Android中,主要提供了MemoryFile这个类来供应用使用匿名共享内存。在Android应用程序框架层,提供了一个MemoryFile接口来封装了匿名共享内存文件的创建和使用,通过JNI调用底层C++方法。
下面是通过Binder进程间通信机制来实现进程间的内存共享的例子。
服务端
public class MainActivity extends Activity {
private MemoryFile mMemoryFile;
private final int MEMORY_SIZE = 3133440 + 1;
private byte[] buffer;
Binder mBinder;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = (Binder) service;
}
public void onServiceDisconnected(ComponentName className) {
mBinder = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
Intent it = new Intent(this, MainService.class);
startService(it);
bindService(it, mConnection, Context.BIND_AUTO_CREATE);
try {
// 参数1文件名,可为null,参数2文件长度
mMemoryFile = new MemoryFile("test", MEMORY_SIZE);
} catch (IOException e) {
e.printStackTrace();
}
}
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
public void write(View v){
EditText et = (EditText) findViewById(R.id.et);
buffer = et.getText().toString().getBytes();
try {
// 写一次 , 读取数据后 数据会被清空
// 持续写,不读,数据不会清空,注意数据覆盖(offset值)
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);
if(mBinder!=null){
//data.writeParcelable(pfd, 0);
// 或者
data.writeFileDescriptor(fileDescriptor);
mBinder.transact(0, data, reply, 0);
}
}
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "写失败", 0).show();
}
}
@Override
protected void onDestroy() {
if(mMemoryFile != null){
mMemoryFile.close();
mMemoryFile = null;
}
unbindService(mConnection);
mConnection = null;
super.onDestroy();
}
}
public class MainService extends Service {
ParcelFileDescriptor pfd;
@Override
public IBinder onBind(Intent arg0) {
return new MyBinder();
}
class MyBinder extends Binder {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply,int flags) throws RemoteException {
switch (code) {
case 0:
//pfd = data.readParcelable(null);
// 或者
pfd = data.readFileDescriptor();
break;
case 1:
//reply.writeParcelable(pfd,0);
// 或者
reply.writeFileDescriptor(pfd.getFileDescriptor());
break;
default:
break;
}
//
return true;
}
}
}
客户端
public class MainActivity extends Activity {
private final int MEMORY_SIZE = 3133440 + 1;
private byte[] buffer = new byte[20];
IBinder mBinder;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// 非服务创建进程,获取的Binder只是一个代理Binder对象,不能直接转换
// mBinder = (Binder) service;
mBinder = service;
}
public void onServiceDisconnected(ComponentName className) {
mBinder = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
Intent it = new Intent("com.xzc.memoryfilewrite.MainService");
startService(it);
bindService(it, mConnection, Context.BIND_AUTO_CREATE);
}
public void read(View v) {
TextView tv = (TextView) findViewById(R.id.tv);
FileInputStream fi=null;
FileDescriptor fileDescriptor=null;
try {
if (mBinder != null) {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
mBinder.transact(1, data, reply, 0);
//ParcelFileDescriptor pfd = reply.readParcelable(null);
// 或者
ParcelFileDescriptor pfd = reply.readFileDescriptor();
if(pfd==null){
buffer = "ParcelFileDescriptor 空指针".getBytes();
tv.setText(new String(buffer));
return;
}
fileDescriptor = pfd.getFileDescriptor();
fi = new FileInputStream(fileDescriptor);
fi.read(buffer);
}
} catch (RemoteException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(fileDescriptor!=null){
try {
fi.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
tv.setText(new String(buffer));
}
@Override
protected void onDestroy() {
unbindService(mConnection);
mConnection = null;
super.onDestroy();
}
}