匿名共享內存(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();
}
}