简介
在Android系统开发中经常会碰到
server
端和client
语言不同问题,例如使用C++编写的Service,客户端是Java/Kotlin;或者是app中创建的Service,client端是c++的情况,本篇文章介绍使用C/C++编写的程序如何与Java编写的Service进行binder
通信。
- 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();
}
- 编写脚本用于生成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,用于做类型转换的代理类。
- 实现
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();
}
- 编写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
- 调试
- 将Server端的app运行起来
- 将client端放入aosp环境编译,产物推到
system/bin/
目录下并运行
网友评论