通過binder實現系統和app匿名記憶體共享
阿新 • • 發佈:2018-12-13
系統和app資料互動的方式有很多種,如:jni、socket、binder等
這個方法都各有優缺點
1、jni
優點:直接呼叫,訪問快
缺點:程式碼量大,至少需要實現本地server、jni及java本地呼叫三部分的程式碼
2、socket
優點:基於C/S架構,程式碼量較少
缺點:需要兩次記憶體拷貝,效率低下
3、binder
優點:基於C/S架構,程式碼量較少,只進行一次記憶體拷貝,相對於socket要高效
缺點:傳輸的資料量大小有限制(核心4M,上層1M-8k)
參考見:https://developer.android.com/reference/android/os/TransactionTooLargeException.html
如果又想使用binder,但傳輸的資料量又超出了1M,這時就得使用匿名記憶體共享了
直接上程式碼
系統原生代碼
/getIFrame.h/
namespace android{ enum { SRV_CODE=2002, CB_CODE=2003, SEND_DATA=2004, SHARE_MEM=3001 }; #define GETIFRAME_SERVICE_DES "MYServiceOsProtocol" #define GETIFRAME_ASHMEM "MYGetIFrameAshmem" struct GetIFrame:public BBinder { private: sp<IBinder> mBinder; int mSharedFd = 0; void* mSharedBuf; virtual status_t onTransact(uint32_t code, const Parcel & data, Parcel * reply, uint32_t flags = 0){ switch(code){ cose CB_CODE: return getIFrame(); default: return BBinder::onTransact(code, data, reply, flags); } } public: GetIFrame(); ~GetIFrame(); int allocAshmemBuffer(int size); void sendIframeToApp(int share_fd, int32_t size); status_t getIFrame(); }; }
/getIFrame.cpp/
GetIFrame::GetIFrame(){ sp<IServiceManager> sm = defaultServiceManager(); mBinder = sm->getService(String16(GETIFRAME_SERVICE_DES)); if(mBinder != NULL){ Parcel data, reply; data.writeStrongBinder(this); mBinder->transace(SRV_CODE, data, &reply, 0); } } GetIFrame::~GetIFrame(){ if(mSharedFd > 0){ ::munmap(mSharedBuf, msize); ::close(mSharedFd); } if(mBinder != NULL){ mBinder = NULL; } } void GetIFrame::sendIFrameToApp(int share_fd, int32_t size){ if(mBinder != NULL){ Parcel data, reply; data.writeInt32(size); data.writeFileDescriptor(share_fd); mBinder->transact(SHARE_MEM,data, &reply, 0); } } int GetIFrame::allocAshmemBuffer(int size){ int result; int ashmemfd = ashmem_create_region(GETIFRAME_ASHMEM, size); if(ashmemfd > 0){ result = ashmem_set_prot_reginon(ashmemfd, PROT_READ | PROT_WRITE); if(result >= 0){ mSharedBuf = ::mmap(NULL, size, PROT_READ | PROT_WRITE, ashmemfd, 0); if(mSharedBuf == MAP_FAILED){ ::close(ashmemfd); ashmemfd = -1; } } } return ashmemfd; } status_t GetIFrame::getIFrame(){ status_t ret = -1; if(mHandle != NULL){ saveIFrame(mHandle); mSharedFd = allocAshmemBuffer(msize); sendIFrameToApp(mSharedFd, msize); } }
app端關鍵程式碼
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags){
…………
switch(code){
case SRV_CODE:
mBinder = data.readStrongBinder();
break;
case SHARE_MEM:
int size = data.readInt();
byte[] buffer= new byte[size];
try{
FileDescriptor fd = data.readFileDescriptor();
if(fd != null){
mMemoryFile = new MemoryFile(fd, size, "r");
}
}catch(IOException ex){
Log.e(TAG,"Failed to create memory file!");
ex.printStrackTrace();
}catch(RemoteException ex){
Log.e(TAG,"Failed to get file descriptor from memory service!");
ex.printStrackTrace();
}
mMemoryFile.readBytes(buffer, 0, 0, size);
break;
}
}
這樣本地系統服務中的大塊資料就直接傳遞給上層的app了,是不是很簡單?