添加 Native 系统服务回调
本文配套源码下载地址:github.com/yuandaimaah...
首先我们要明确的是无论是从 client 调用 server, 还是 server 回调 client,本质上都是跨进程通信,都是需要借助 binder 框架的。
接下来我们在Binder 程序示例之 aidl-cpp 篇介绍的示例程序上修改,给它添加回调功能。
首先我们在我们的自定义 Product device/jelly/rice14
下创建如下的文件与文件夹(关于自定义 Product,请查看 添加 Product):
bash
AIDLCppCallbackDemo
├── aidl.sh
├── Android.bp
├── com
│ └── yuandaima
│ ├── ICallback.aidl
│ └── IHello.aidl
├── HelloClient.cpp
└── HelloServer.cpp
AIDL 文件编写
其中 ICallback.aidl
是我们定义的回调接口,其内容如下:
java
package com.yuandaima;
interface ICallback
{
void onCallback(String str);
}
接着我们通过如下命令,将 aidl 转换为对应的 cpp 源码:
bash
# 第一个路径是头文件位置,会在这个路径下创建包对应的文件夹
# 第二路径是 cpp 文件路径
aidl-cpp com/yuandaima/ICallback.aidl ./ ./ICallback.cpp
通过上述命令生成如下的文件结构:
bash
AIDLCppCallbackDemo
├── aidl.sh
├── Android.bp
├── com
│ └── yuandaima
│ ├── BnCallback.h
│ ├── BpCallback.h
│ ├── ICallback.aidl
│ ├── ICallback.h
│ └── IHello.aidl
├── HelloClient.cpp
├── HelloServer.cpp
└── ICallback.cpp
IHello.aidl
是我们定义的 binder 服务端对外提供的服务接口,其内容如下:
bash
package com.yuandaima;
import com.yuandaima.ICallback;
interface IHello
{
void hello();
int sum(int x, int y);
void registerCallback(ICallback cb);
}
接着我们通过如下命令,将 aidl 转换为对应的 cpp 源码:
bash
# -I 用于指示我们 import 的部分在哪里找
# 第一个路径是头文件位置,会在这个路径下创建包对应的文件夹
# 第二路径是 cpp 文件路径
aidl-cpp -I./com/yuandaima com/yuandaima/IHello.aidl ./ ./IHello.cpp
通过上述命令生成如下的文件结构:
bash
AIDLCppCallbackDemo
├── aidl.sh
├── Android.bp
├── com
│ └── yuandaima
│ ├── BnCallback.h
│ ├── BnHello.h
│ ├── BpCallback.h
│ ├── BpHello.h
│ ├── ICallback.aidl
│ ├── ICallback.h
│ ├── IHello.aidl
│ └── IHello.h
├── HelloClient.cpp
├── HelloServer.cpp
├── ICallback.cpp
└── IHello.cpp
为了方便,我们把两个命令写为一个 shell 脚本 aidl.sh
,后面修改了 aidl 文件,直接使用 shell 脚本即可生成新的源码文件了
bash
#!/bin/bash
aidl-cpp com/yuandaima/ICallback.aidl ./ ./ICallback.cpp
aidl-cpp -I./com/yuandaima com/yuandaima/IHello.aidl ./ ./IHello.cpp
服务端实现
服务端实现在 HelloServer.cpp
中:
cpp
#define LOG_TAG "aidl_cpp"
#include <log/log.h>
#include <stdlib.h>
#include <utils/RefBase.h>
#include <utils/Log.h>
#include <binder/TextOutput.h>
#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include "com/yuandaima/IHello.h"
#include "com/yuandaima/BnHello.h"
#include "com/yuandaima/ICallback.h"
#include "com/yuandaima/BpCallback.h"
using namespace android;
//定义服务端
class IHelloServer : public com::yuandaima::BnHello
{
private:
//回调接口
sp<com::yuandaima::ICallback> mCallback;
public:
binder::Status hello() override
{
ALOGI("hello");
return binder::Status();
}
binder::Status sum(int32_t v1, int32_t v2, int32_t *_aidl_return) override
{
ALOGI("server: sum: %d + %d", v1, v2);
*_aidl_return = v1 + v2;
//使用回调接口
if(mCallback.get() != nullptr) {
mCallback->onCallback(String16("str from server"));
}
return binder::Status();
}
//注册回调接口
binder::Status registerCallback(const sp<::com::yuandaima::ICallback>& cb) override
{
ALOGI("Server registerCallback");
mCallback = cb;
return binder::Status();
}
};
int main(int argc, char const *argv[])
{
ALOGD("HelloServer is running");
defaultServiceManager()->addService(String16("IHello"), new IHelloServer());
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
}
首先我们需要定义我们的服务端类 IHelloServer
,这个类继承自 BnHello
,这个类来自 aidl 生成的头文件 com/yuandaima/BnHello.h
。我们需要实现三个函数的服务端实现,我们重点关注 registerCallback
,该函数用于组成回调接口,会把回调对象保存在成员变量 mCallback 中,当我们的调用到 sum 函数时,就会通过 mCallback 成员调用回调函数了。
客户端实现
cpp
#define LOG_TAG "aidl_cpp"
#include <log/log.h>
#include <stdlib.h>
#include <utils/RefBase.h>
#include <utils/Log.h>
#include <binder/TextOutput.h>
#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include "com/yuandaima/IHello.h"
#include "com/yuandaima/BpHello.h"
#include "com/yuandaima/ICallback.h"
#include "com/yuandaima/BnCallback.h"
using namespace android;
class MyCallback : public com::yuandaima::BnCallback {
public:
binder::Status onCallback(const String16 &str) override {
ALOGD("client: onCallback, receive str: %s", String8(str).string());
return binder::Status();
}
};
int main(int argc, char const *argv[])
{
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("IHello"));
sp<com::yuandaima::IHello> hello = interface_cast<com::yuandaima::IHello>(binder);
hello->hello();
sp<MyCallback> myCallback = new MyCallback();
hello->registerCallback(myCallback);
int ret = 0;
hello->sum(1, 2, &ret);
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
}
与之前的示例 不同的是:
- 实现
MyCallback
类,其父类为BnCallback
,来自生成的源码com/yuandaima/BnCallback.h
,在类中需要实现对应的回调函数,这里简单打印一些信息。 - 主函数中需要开启线程池,因为 Client 端需要等待 Server 端的回调,所以需要开启线程池,让程序持续运转下去。
编译与测试
编写 Android.bp 编译文件:
json
cc_binary {
name: "BinderCallbackServer",
srcs: ["HelloServer.cpp", "IHello.cpp","ICallback.cpp"],
shared_libs: [
"liblog",
"libcutils",
"libutils",
"libbinder",
],
}
cc_binary {
name: "BinderCallbackClient",
srcs: ["HelloClient.cpp", "IHello.cpp","ICallback.cpp"],
shared_libs: [
"liblog",
"libcutils",
"libutils",
"libbinder",
],
}
接着在项目目录下执行 mm
命令,编译当前模块。接着我们把模拟器打开,然后把可执行文件 push 到模拟器上:
bash
adb push out/target/product/rice14/system/bin/BinderCallbackServer /data/local/tmp
adb push out/target/product/rice14/system/bin/BinderCallbackClient /data/local/tmp
接着进入模拟器 shell 环境,执行可执行文件:
bash
adb shell
cd /data/local/tmp/
#执行服务端
./BinderCallbackServer &
#执行客户端
./BinderCallbackClient &
接着查看 log:
从 log 的内容可以看出,我们的回调函数执行成功了。