簡介
在Android系統(tǒng)開發(fā)中經(jīng)常會碰到
server
端和client
語言不同問題,例如使用C++編寫的Service洽故,客戶端是Java/Kotlin;或者是app中創(chuàng)建的Service,client端是c++的情況广恢,本篇文章介紹使用C/C++編寫的程序如何與Java編寫的Service進行binder
通信欣孤。
- Binder通信首先創(chuàng)建
AIDL
文件馋没,用于定義服務(wù)端的接口,這里簡單示例:
// 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 生成相關(guān)的類)
cc_library_shared {
name: "lib-test",
srcs:["./**/*.aidl"],
aidl:{
include_dirs:["./"],
},
shared_libs:[
"libutils",
"libcutils",
"libbinder",
],
}
我們當(dāng)前的目錄結(jié)構(gòu)如下:
├── 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
端的頭文件,需要我們?nèi)崿F(xiàn)婆排;Bp
打頭的文件類似于Java中的Stub声旺,用于做類型轉(zhuǎn)換的代理類。
- 實現(xiàn)
Bn
段只,在這個例子中腮猖,我們是需要雙向通信的,client端獲取ItestService
訪問server端赞枕,同時像server端注冊ITestClient
澈缺,server端通過ITestClient
可以訪問client
端;
- Server端通過Java實現(xiàn)
ITestService
,使用AS創(chuàng)建一個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++實現(xiàn)
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
創(chuàng)建一個文件TestClient.h
#ifndef BINDERTEST_TESTCLIENT_H
#define BINDERTEST_TESTCLIENT_H
#include "com/lu/test/BnTestClient.h"
//此處繼承的是BnTestClient谍椅,這個類幫助我們實現(xiàn)了binder接口的轉(zhuǎn)化
class TestClient : public ::com::lu::test::BnTestClient {
public:
TestClient();
virtual ~TestClient();
::android::binder::Status getClientName(::android::String16 *_aidl_return);
};
#endif //BINDERTEST_TESTCLIENT_H
創(chuàng)建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句是使當(dāng)前線程具有binder的能力,會阻塞住當(dāng)前線程古话,建議可以放到子線程中
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",
],
}
目錄結(jié)構(gòu)
├── 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
- 調(diào)試
- 將Server端的app運行起來
- 將client端放入aosp環(huán)境編譯雏吭,產(chǎn)物推到
system/bin/
目錄下并運行