Android native進程間通訊實例-binder篇之——用parcel傳輸數組

 和以前稍微不一樣,此次要稍微分析一下 Parce.cpp 和 android_os_Parcel.cp p的源碼,爲的是可以掌握調試技巧,後續傳輸其它類型數據就能觸類旁通了!
 
1. 代碼共享

此次不貼Android.mk代碼了,直接沿用以前寫的便可,傳送門 http://www.javashuo.com/article/p-soutduzp-eg.htmlhtml

a. 服務端mybinderserver.cpp代碼以下:java

#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>

#include<stdio.h>

#define LOG_TAG "binderserver"

using namespace android;

class MyBinderService : public BBinder{
    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    {

        printf("MyBinderService onTransact code = %d\n", code);
        int readLen = 0;
        float *readPencilData = NULL;
        
        if(code == 123)
        {
            readLen = data.readInt32();
            readPencilData = (float *)malloc(readLen*sizeof(float));
            readPencilData = (float *)data.readInplace(readLen);
            for(int i = 0; i < readLen; i++)
            {
                printf("readPencilData[%d] = %f \n", i, readPencilData[i]);
            }

            free(readPencilData);
            readPencilData = NULL;

        }
        printf("return NO_ERROR\n");
        
        return NO_ERROR;
    }
};

int main(int argc, char** argv)
{
    sp<IBinder> serverBinder = new MyBinderService();

    defaultServiceManager()->addService(String16("mybindertag"), serverBinder);
    printf("main addService \n");
    
    sp<ProcessState> proc(ProcessState::self());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();

    printf("never return!!! \n");
    
    return 0;
}

 

b. 客戶端mybinderclient.cpp代碼以下:android

#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>

#include<stdio.h>

#define LOG_TAG "binderclient"

using namespace android;

#define WRITESARRYSIZE 10

int main(int argc, char** argv)
{
    sp<IServiceManager> sm = defaultServiceManager();
    sp<IBinder> binder = sm->checkService(String16("mybindertag"));

    float writeArry[WRITESARRYSIZE] = {123.123, 234.234, 345.345, 456.456, 567.567,
                                                             678.678, 789.789, 890.890, 901.901, 012.012};

    if (binder == 0)
    {
        printf("service is not found !\n");
        return 0;
    }
    else
    {
        sp<IBinder> binder = defaultServiceManager()->getService(String16("mybindertag"));
    }

    while(1)
    {
        Parcel data, reply;

        int transCode = 0;
        int writeInt = 0; 
        int replyInt = 0;

        printf("please input transCode : \n");
        scanf("%d", &transCode);
        getchar();

        if(123 == transCode)
        {
            data.writeInt32(WRITESARRYSIZE);
            status_t ret = 0;
            ret = data.write((void *)writeArry, WRITESARRYSIZE*sizeof(float));
            if(ret != NO_ERROR)
                perror("trans failed!!");
        }

        binder->transact(transCode, data, &reply);

        replyInt = reply.readInt32();
        printf("get reply data = %d\n", replyInt);
        
    }
    return 0;
}

  

測試效果:數組

 

 

 

 2. 源碼分析函數

首先講一個故事,我以前不知道binder能直接傳數據塊,一直都是用客戶端一個個數據寫,而後服務端一個個數據讀的低效率模式。源碼分析

後來android系統層的一位同事告訴我,java層binder能夠直接用 writeByteArray來傳輸數組等大塊數據,傳一次就行,聽的我面紅耳赤,post

看來平時研究的少確實會影響代碼的執行效率啊。測試

 

a. 突破口 writeByteArrayui

在 framework 代碼中搜索 cpp 文件,執行命令:spa

grep -rn "writeByteArray" --include "*.cpp" ./frameworks/

發如今/frameworks/native/libs/binder/Parcel.cpp 有這個函數的實現,可是進去看之後大失所望,由於沒有readByteArray的實現,在native層

我總不能只會寫不會讀吧。

 

b. 不賣關子了,直接打開全部相關源碼,一目瞭然

./frameworks/base/core/java/android/os/Parcel.java

./frameworks/base/core/jni/android_os_Parcel.cpp

./frameworks/native/libs/binder/Parcel.cpp

能夠很清晰的知道調用過程,因爲代碼量比較小就不畫流程框圖了:

writeByteArray:Parcel.java(writeByteArray) -> android_os_Parcel.cpp(android_os_Parcel_writeNative) -> Parcel.cpp(writeInt32) -> Parcel.cpp(writeInplace)

readByteArray:Parcel.java(readByteArray) -> android_os_Parcel.cpp(android_os_Parcel_createByteArray) -> Parcel.cpp(readInplace

 

c. 選取native層能夠用的函數直接用上

在 android_os_Parcel_writeNative 中,寫數組數據,先要用 writeInt32 要寫入的數據大小,而後再 writeInplace 返回一個地址,接着把要傳輸的數據 memcpy 到這個地址上,好奇的我發現

 writeInplace + memcpy 的操做其實就是在Parcel.cpp源碼 status_t Parcel::write(const void* data, size_t len)的操做,因此後續寫數組,直接用 Parcel::write 便可

至於 readInplace 就沒啥好說的了,直接傳入要讀的數據塊大小,返回一個地址,取數據就好了。

 

大概分析思路就是這樣子了,後續要傳輸別的數據類型,直接參考這個模式便可。可是binder 傳輸數據有大小限制,分不一樣狀況限制不一樣,總之一次性仍是不能傳無限大的數據,傳個

小圖片足夠就好了。具體限制多少能夠參考網上其它的博客。

 

但願你們多多吐槽,你們一塊兒共同進步!!

相關文章
相關標籤/搜索