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


 和之前稍微不同,這次要稍微分析一下 Parce.cpp 和 android_os_Parcel.cp p的源碼,為的是能夠掌握調試技巧,后續傳輸其它類型數據就能舉一反三了!
 
1. 代碼共享

這次不貼Android.mk代碼了,直接沿用之前寫的即可,傳送門 https://www.cnblogs.com/songsongman/p/11097196.html

a. 服務端mybinderserver.cpp代碼如下:

#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代碼如下:

#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來傳輸數組等大塊數據,傳一次就行,聽的我面紅耳赤,

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

 

a. 突破口 writeByteArray

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

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 傳輸數據有大小限制,分不同情況限制不同,總之一次性還是不能傳無限大的數據,傳個

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

 

希望大家多多吐槽,大家一起共同進步!!

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM