美文网首页
C++与Java之间的Binder通信

C++与Java之间的Binder通信

作者: 好多个胖子 | 来源:发表于2023-11-27 11:20 被阅读0次

    简介

    在Android系统开发中经常会碰到server端和client语言不同问题,例如使用C++编写的Service,客户端是Java/Kotlin;或者是app中创建的Service,client端是c++的情况,本篇文章介绍使用C/C++编写的程序如何与Java编写的Service进行binder通信。

    1. Binder通信首先创建AIDL文件,用于定义服务端的接口,这里简单示例:
    // server
    package com.lu.test;
    import com.lu.test.ITestClient;
    
    interface ITestService{
        String getServiceName();
        void registerClient(ITestClient client);
    }
    
    //client
    package com.lu.test;
    
    interface ITestClient{
        String getClientName();
    }
    
    1. 编写脚本用于生成c层的头文件(java可以通过Android Studio 生成相关的类)
      cc_library_shared {
        name: "lib-test",
        srcs:["./**/*.aidl"],
        aidl:{
            include_dirs:["./"],
        },
        shared_libs:[
            "libutils",
            "libcutils",
            "libbinder",
        ],
    }
    

    我们当前的目录结构如下:

    ├── Android.bp
    └── com
        └── lu
            └── test
                ├── ITestClient.aidl
                └── ITestService.aidl
    
    

    将该部分文件放入到aosp下,可以放在vendor底下,然后运行在根目录运行:

    make lib-test
    

    即可得到头文件:

    # 生成的头文件路径
    # out/soong/.intermediates/vendor/test/lib-test/android_arm64_armv8-a_shared/gen
    
    生成的文件列表
    
    .
    └── com
        └── lu
            └── test
                ├── BnTestClient.h
                ├── BnTestService.h
                ├── BpTestClient.h
                ├── BpTestService.h
                ├── ITestClient.cpp
                ├── ITestClient.cpp.d
                ├── ITestClient.h
                ├── ITestService.cpp
                └── ITestService.h
    

    其中Bn开头的是作为Binder中的server端的头文件,需要我们去实现;Bp打头的文件类似于Java中的Stub,用于做类型转换的代理类。

    1. 实现Bn,在这个例子中,我们是需要双向通信的,client端获取ItestService访问server端,同时像server端注册ITestClient,server端通过ITestClient可以访问client端;
    • Server端通过Java实现ITestService,使用AS创建一个App,并且编写一个Service即可:
    private const val TAG = "TestService"
    
    class TestService : Service() {
    
        private val mService = object : ITestService.Stub() {
            override fun getServiceName(): String {
                return "TestService";
            }
    
            override fun registerClient(client: ITestClient) {
                Log.d(TAG, "registerClient : ${client.clientName}")
            }
        }
    
        override fun onBind(intent: Intent?): IBinder? {
            return mService
        }
    
        override fun onCreate() {
            super.onCreate()
            //将service添加到ServiceManager管理中
            ServiceManager.addService("BinderTest", mService)
        }
    }
    
    • Client端通过C++实现ITestClient
      首先我们看下通过AIDL生成的ITestClient.h
    #pragma once
    
    #include <binder/IBinder.h>
    #include <binder/IInterface.h>
    #include <binder/Status.h>
    #include <utils/String16.h>
    #include <utils/StrongPointer.h>
    
    namespace com {
    
    namespace lu {
    
    namespace test {
    
    class ITestClient : public ::android::IInterface {
    public:
      DECLARE_META_INTERFACE(TestClient)
      virtual ::android::binder::Status getClientName(::android::String16* _aidl_return) = 0;
    };  // class ITestClient
    
    class ITestClientDefault : public ITestClient {
    public:
      ::android::IBinder* onAsBinder() override {
        return nullptr;
      }
      ::android::binder::Status getClientName(::android::String16*) override {
        return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
      }
    };  // class ITestClientDefault
    
    }  // namespace test
    
    }  // namespace lu
    
    }  // namespace com
    

    创建一个文件TestClient.h

    #ifndef BINDERTEST_TESTCLIENT_H
    #define BINDERTEST_TESTCLIENT_H
    
    #include "com/lu/test/BnTestClient.h"
    
    //此处继承的是BnTestClient,这个类帮助我们实现了binder接口的转化
    class TestClient : public ::com::lu::test::BnTestClient {
    public:
        TestClient();
    
        virtual ~TestClient();
    
        ::android::binder::Status getClientName(::android::String16 *_aidl_return);
    };
    
    #endif //BINDERTEST_TESTCLIENT_H
    
    

    创建TestClient.cpp

    #include "TestClient.h"
    
    using namespace com::lu::test;
    
    TestClient::TestClient() = default;
    
    TestClient::~TestClient() = default;
    
    ::android::binder::Status TestClient::getClientName(::android::String16* _aidl_return){
        *_aidl_return = android::String16("TestClient");
       return android::binder::Status::ok();
    }
    
    1. 编写client端的测试程序TestMain.cpp
    #include <unistd.h>
    #include "binder/IBinder.h"
    #include "utils/StrongPointer.h"
    #include "binder/IServiceManager.h"
    #include <android/binder_manager.h>
    #include <android/binder_process.h>
    #include "com/lu/test/ITestService.h"
    #include "android_log_define.h"
    #include "TestClient.h"
    #include "thread"
    
    #define SERVER_NAME  "BinderTest"
    
    using namespace std;
    using namespace android;
    
    TestClient *clientImpl = new TestClient();;
    
    android::sp<com::lu::test::ITestService> getService() {
        sp<IServiceManager> sm = defaultServiceManager();
        if (sm == nullptr) {
            LOGE("can't get serviceManager");
            return nullptr;
        }
        auto binder = sm->getService(String16(SERVER_NAME));
        if (binder == nullptr) {
            LOGE("can not get binder");
            return nullptr;
        }
    
        auto logServer = interface_cast<com::lu::test::ITestService>(binder);
        if (logServer == nullptr) {
            LOGE("can't cast LogServer");
            return nullptr;
        }
    
        return logServer;
    }
    
    int main() {
        auto service = getService();
        if (service == nullptr) {
            LOGE("registerService failed service is null");
            return -1;
        }
        service->registerClient(clientImpl);
        auto name = new String16();
        service->getServiceName(name);
        LOGD("the service name is %s", name->string());
        //这2句是使当前线程具有binder的能力,会阻塞住当前线程,建议可以放到子线程中
        ABinderProcess_setThreadPoolMaxThreadCount(0);
        ABinderProcess_joinThreadPool();
    }
    
    

    编译脚本

    cc_binary {
        name: "BindClientTest",
        srcs:[
            "./**/*.cpp"
        ],
        local_include_dirs:[
            "./include",
        ],
        shared_libs:[
            "libutils",
            "libcutils",
            "libbinder",
            "liblog",
            "libbase",
            "libbinder_ndk"
        ],
        cflags: [
            "-Wall",
            "-Werror",
            "-Wextra",
            "-Wno-unused-parameter",
            "-std=c++11",
            "-frtti",
            "-fexceptions",
            "-fPIC",
        ],
    }
    

    目录结构

    ├── Android.bp
    ├── TestClient.cpp
    ├── TestMain.cpp
    └── include
        ├── TestClient.h
        ├── android_log_define.h
        └── com
            └── lu
                └── test
                    ├── BnTestClient.h
                    ├── BnTestService.h
                    ├── BpTestClient.h
                    ├── BpTestService.h
                    ├── ITestClient.cpp
                    ├── ITestClient.cpp.d
                    ├── ITestClient.h
                    ├── ITestService.cpp
                    └── ITestService.h
    
    
    1. 调试
    • 将Server端的app运行起来
    • 将client端放入aosp环境编译,产物推到system/bin/目录下并运行

    相关文章

      网友评论

          本文标题:C++与Java之间的Binder通信

          本文链接:https://www.haomeiwen.com/subject/xlmzwdtx.html