• Android native进程间通信实例-binder篇之——简单的单工通信


        网上找了很多binder相关文章,大部分都是在跟踪binder实现源代码,然后再把框架代码贴出来,看着实在费力。

        这篇文章从实际出发,直接用一个案例下手,后续想了解binder相关原理的话,可以参考《深入理解Android》或者其它博客。

        如果有疑问可以在下方评论,博主会根据自己的认知程度来回复的。

    (小提示:要会使用binder通信,其实只需要了解binder通信有一个服务端和客户端,服务端创建特定字符串,然后客户端通过这个特定字符串找到服务端,进行客户端对服务端的通信。)

    1. 代码共享

    话不多说直接贴上已经经过调试ok的代码,代码不过50行,看起来应该不那么费力吧!

    a. 首先是服务端Android.mk代码:

    LOCAL_PATH:= $(call my-dir)
    
    include $(CLEAR_VARS)
    
    #需要编译的cpp文件
    LOCAL_SRC_FILES:= mybinderserver.cpp
    
    LOCAL_C_INCLUDES := 
      external/skia/include/core 
      bionic 
      external/stlport/stlport
    
    #编译为可执行文件      
    LOCAL_MODULE:= mybinderserver 
            
    LOCAL_MODULE_TAGS := optional
    
    #添加依赖库一定要有libbinder      
    LOCAL_SHARED_LIBRARIES := 
      libcutils  
      libutils 
      libbinder 
      libgui 
      libskia 
      libui     
          
    include $(BUILD_EXECUTABLE) 

    b. 然后是服务端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);
    
            if(code == 123)
            {
                int readInt = 0;
                
                readInt = data.readInt32();
                
                printf("MyBinderService onTransact readInt = %d
    ", readInt);
                
                reply->writeInt32(234);
            }
            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;
    }
    

      

    c. 然后是客户端Android.mk:

    LOCAL_PATH:= $(call my-dir)
    
    include $(CLEAR_VARS)
    
    #需要编译的cpp文件
    LOCAL_SRC_FILES:= mybinderclient.cpp
    
    LOCAL_C_INCLUDES := 
      external/skia/include/core 
      bionic 
      external/stlport/stlport
    
    #编译为可执行文件      
    LOCAL_MODULE:= mybinderclient 
            
    #LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
    LOCAL_MODULE_TAGS := optional
    
    #添加依赖库一定要有libbinder      
    LOCAL_SHARED_LIBRARIES := 
      libcutils  
      libutils 
      libbinder 
      libgui 
      libskia 
      libui     
          
    include $(BUILD_EXECUTABLE) 
    

     

    d. 最后是客户端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;
    
    int main(int argc, char** argv)
    {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder = sm->checkService(String16("mybindertag"));
    
        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)
            {
                printf("please input int you need transfer: 
    ");
                scanf("%d", &writeInt);
                getchar();
                data.writeInt32(writeInt);
            }
            binder->transact(transCode, data, &reply);
    
            replyInt = reply.readInt32();
            printf("get reply data = %d
    ", replyInt);
            
        }
        return 0;
    }
    

     

    e. 编译这两个文件,把可执行文件mybinderserver和mybinderclient通过adb push 推入到设备的/system/bin目录下,

    有的新手可能不了解怎么编译可执行文件,这里稍微科普一下操作方法,比如以mybinderserver为例吧,

    在 frameworksasecmds 创建相应的文件夹mybinderserver,把Android.mk和mybinderserver.cpp拷贝进去

    编译的时候

    1. 执行. build/envsetup.sh

    2. lunch 选择对应的版本

    3. mmm frameworks/base/cmds/mybinderserver/

    4. adb root

    5. adb remount

    6. adb push out/target/product/rk3368/system/bin/mybinderserver system/bin

    同理mybinderclient 也是这样操作。

    f. 打开两个终端,进入adb shell

    首先服务端执行可执行文件:mybinderserver

    然后客户端执行可执行文件:mybinderclient 输入相应指令,通过printf输出可知通信数据传输正常。

    2. 源码分析

    a. 服务端

    从main函数开始看,这里会new一个继承BBinder的类,名字叫做MyBinderService,然后addService里面填参数,一个是标识服务的字符串mybindertag,还有一个就是传输数据会用到的MyBinderService类。

    然后通过下面三行,加入到线程池中,让这个可执行文件不会返回退出。

    sp<ProcessState> proc(ProcessState::self());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();

    MyBinderService类中,会实现onTransact,这个是标准接口来着,status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

    1. code 是标识传输的标志,一般服务端会用switch语句来处理多个code数据处理请求,我这里就简单用if语句判断code然后做相关操作。

    2. Parcel 这个数据类用于binder传输,它的实现在framework/native/libs/binder下面,后续会介绍实现大块数据传输的案例,目前只是做int类型的传输。

    其中data里面包含了传输的数据,可以通过readInt32读出数据,reply用来反馈,这里用writeInt32来写数据。

    3. flags 有多个,目前用默认的阻塞模式,这样能够保证数据传输的完整性,可以看我的客户端程序,没有传参数,一般可以传IBinder::FLAG_ONEWAY,这样保证了传输速度,但是有掉数据的风险。

    b. 客户端

    1. 首先确认能否在系统的binder服务列表中寻找到以 mybindertag 为标识的服务。用到了checkService。

    2. 然后再getService,返回给一个本地创建的 binder 指针,接着就可以用这个 binder 指针做传输数据了。

    3. 同样是用Parcel 这个数据类,writeInt32写数据,写完以后通过binder->transact(transCode, data, &reply);来传输数据。

    刚才说了在onTransact的第四个参数可以默认不填,但是有些情况下要完成特地功能,比如传输要保证速度可以这样传 binder->transact(transCode, data, &reply, IBinder::FLAG_ONEWAY); 有掉数据的风险,慎用。

    最后推荐一个调试 binder服务的命令:service 。

    就拿 mybinderserver 为例,打开两个终端。其中一个终端运行 mybinderserver。
    另一个终端在adb shell 下执行命令:
    service list | grep my
    发现不用输入完整的 mybinderserver 就筛选出了已经注册的服务mybindertag,下图说明服务已经在运行了。而且这个命令会填写code = 1598968902,应该和命令的实现方式有关,这里不做深究。

    如果是系统服务还可以直接命令行通信service call xxx 具体用法可以参考网上其它案例,我写的服务是临时创建的服务,没有注册到系统服务中,所以不能用service call来调试。

    基本实现就是这样了,希望大家多多吐槽,大家一起共同进步!!

  • 相关阅读:
    php数组函数-array_push()
    php数组函数-array_pop()
    php数组函数-array_pad()
    php数组函数-array_merge()
    php数组函数-array_map()
    php数组函数-array_keys()
    php数组函数-array_key_exists()
    php数组函数-array_intersect()
    php数组函数-array_flip()
    php数组函数-array_filter()
  • 原文地址:https://www.cnblogs.com/songsongman/p/11097196.html
Copyright © 2020-2023  润新知