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

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

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

 

    若是有疑问能够在下方评论,博主会根据本身的认知程度来回复的。框架

 

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

 

1. 代码共享函数

 

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

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

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代码:ui

#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);

        if(code == 123)
        {
            int readInt = 0;
            
            readInt = data.readInt32();
            
            printf("MyBinderService onTransact readInt = %d\n", readInt);
            
            reply->writeInt32(234);
        }
        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;
}

  

c. 而后是客户端Android.mk:spa

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 !\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)
        {
            printf("please input int you need transfer: \n");
            scanf("%d", &writeInt);
            getchar();
            data.writeInt32(writeInt);
        }
        binder->transact(transCode, data, &reply);

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

 

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

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

在 frameworks\base\cmds 建立相应的文件夹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来调试。

 

基本实现就是这样了,但愿你们多多吐槽,你们一块儿共同进步!!

相关文章
相关标签/搜索