添加 Native 系统服务回调

添加 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 的内容可以看出,我们的回调函数执行成功了。

相关推荐
拭心10 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
带电的小王12 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡12 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道12 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库13 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道14 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe14 小时前
Android Hook - 动态加载so库
android
居居飒15 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He18 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗18 小时前
Android笔试面试题AI答之Android基础(1)
android