zoukankan      html  css  js  c++  java
  • 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
    ", 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 
    ", i, readPencilData[i]);
                }
    
                free(readPencilData);
                readPencilData = NULL;
    
            }
            printf("return NO_ERROR
    ");
            
            return NO_ERROR;
        }
    };
    
    int main(int argc, char** argv)
    {
        sp<IBinder> serverBinder = new MyBinderService();
    
        defaultServiceManager()->addService(String16("mybindertag"), serverBinder);
        printf("main addService 
    ");
        
        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    
        printf("never return!!! 
    ");
        
        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 !
    ");
            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 : 
    ");
            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
    ", 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 传输数据有大小限制,分不同情况限制不同,总之一次性还是不能传无限大的数据,传个

    小图片足够就行了。具体限制多少可以参考网上其它的博客。

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

  • 相关阅读:
    IDEA工具-快捷键整理
    Intellij热部署插件JRebel_转载
    [刘阳Java]_为什么要前后端分离
    [刘阳Java]_程序员Java编程进阶的5个注意点,别编程两三年还是增删改查
    [刘阳Java]_Web前端入门级练习_迅雷首页第一屏设计
    [刘阳Java]_Web前端入门级练习_迅雷官宣网设计
    [刘阳Java]_MySQL数据优化总结_查询备忘录
    Mybatis总结(一)
    java.io.IOException: Could not find resource com/xxx/xxxMapper.xml
    push to origin/master was rejected错误解决方案
  • 原文地址:https://www.cnblogs.com/songsongman/p/11097216.html
Copyright © 2011-2022 走看看