1. 程式人生 > >通過binder實現系統和app匿名記憶體共享

通過binder實現系統和app匿名記憶體共享

系統和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了,是不是很簡單?