C++ SDK提供回调函数接口给Android/iOS 集成

C++如何提供跨平台的回调函数接口是业务开发很常遇到的需求,但是实际上公开的资料都很杂乱,而且不一定正确,因此在此文章中,以Android Java和iOS Swift为例,系统化说明一下如何制作一个跨平台C++回调函数接口。

对于C++而言,最常用的建构回调函数方法是基于std::function

std::function 是 C++ 标准库中的一个模板类,用于封装函数对象(Function Object)、函数指针、以及可以调用的(如 lambda 表达式)。它提了一种通用的方式来存储、传递和调用各种类型的可调用对象。

std::function 的主要作用是将可调用对象视为一个函数类型,并提供了一组成员函数来操作和调用这个函数对象。它的声明如下:

template <typename Signature>
class function;

其中 Signature 是函数类型的签名,包括返回类型和参数类型。

std::function 的使用非常灵活,可以用于存储各种类型的可调用对象,包括函数指针、成员函数指针、函数对象、以及 lambda 表达式等。可以通过以下方式来创建 std::function 对象:

std::function<int(int)> func;  // 声明一个接受 int 类型参数并返回 int 类型的函数对象

// 将一个 lambda 表达式赋值给 func
func = [](int x) { return x * 2; };

// 调用 func
int result = func(5);  // 调用 lambda 表达式,得到结果 10

std::function 还可以作为函数参数或返回值使用,以实现更灵活的函数接口。

总而言之,std::function 提供了一种通用的封装方式,使得各种可调用对象可以以统一的方式进行存储、传递和调用,提高了代码的灵活性和可重用性。它在函数回调、事件处理等场景下非常有用。

整体思路

因此制作跨平台的回调函数接口也是围绕std::function展开,主要其实就分成两的步骤

1. 使用std::function制作C++ 回调函数接口,以及回调函数触发的代码 

2. 使用目标平台的语言实现方法转换,将目标平台的语言的方法转换成std::function,本文章会介绍使用Android Java和iOS Swift实现转换的代码

1. C++ 回调函数接口

首先在C++声明一个回调函数

std::function<void(std::string, int)> callback_ = nullptr;

然后制作一个回调函数注册接口

void registCallback( std::function<void(std::string, int)>  callback){
            callback_ = callback;
  }

最后在代码中加入触发回调的代码

int func(std::string tag){
    for (int i=0; i<100 ; i++){
        callback_(tag, i);
    }
}

2. 使用Android Java方法作为回调函数

2.1 Java传入的回调方法注册

需要在jni层建构一个Java和C++函数的桥接,首先我们建构的一个Java回调函数注册的接口

jobject jCallback = nullptr;
jmethodID jCallbackMethId = nullptr;
JavaVM *g_pJVM = nullptr
std::mutex g_CallbackMutex;

extern "C" JNIEXPORT int JNICALL
Java_com_test_registerCallback(JNIEnv *env,jclass clazz,object call_back) {
    // 线程保护
    std::unique_lock<std::mutex> lck(g_CallbackMutex);
    // 避免重复注册
    if (jCallback) {
        return -1;
    }

    jCallback = env->NewGlobalRef(call_back);
    jclass callbackClazz = env->GetObjectClass(call_back);
    jCallbackMethId =
            env->GetMethodID(callbackClazz, "onUpdateCallback", "(Ljava/lang/String;I)V");
    env->GetJavaVM(&g_pJVM);

    return 0;
}

具体分成几个步骤,首先是为android端的函数建构一个Reference,这个Reference是Java跟C++通用的地址, 其中callback就是从Java层传入的回调实现

jCallback = env->NewGlobalRef(call_back);
jclass callbackClazz = env->GetObjectClass(call_back);

接著是找到具体方法的id,这里需要名称跟入参出参的类型都正确无误的对应,否则会出现地址错误,详细的说明可以参考这里

jCallbackMethId =
            env->GetMethodID(callbackClazz, "onUpdateCallback", "(Ljava/lang/String;I)V");

2.2 jni层实际的回调触发函数

我们在上面已经建构了一个名叫onUpdateCallback的Java 方法,接著我们需要把这个这个Java方法封装成C++ 方法,并在调用这个C++方法时调用Java方法

void onUpdateCallback(std::string tag, in p) {
    JNIEnv *pNewEnv = nullptr;
    if (jCallback && jCallbackMethId && g_pJVM) {
        if (g_pProgressJVM->AttachCurrentThread(
                reinterpret_cast<JNIEnv **>(&pNewEnv), NULL) < 0) {
            return;
        }
        // std::string 转 jstring
        jstring js = pNewEnv->NewStringUTF(key.c_str());
        // 调用Java方法:onUpdateCallback
        pNewEnv->CallVoidMethod(jPCallback, jCallbackMethId,
                                js, p);
    }
}

接著在jni层找个合适的地方将建构好onUpdateCallback的Java注册到C++就行,

// 某个方法内部,可以是初始化函数,但需要避免会重复调用的方法
...
{
     int ret = registCallback(onProgressCallback);
}
...

2.3 Java层接口封装

回到刚刚在jni建构了一个注册回调函数的接口

Java_com_test_registerCallback(JNIEnv *env,jclass clazz,object call_back) {

这里的object call_back就是我们在Java层实现的回调函数本体,但是因为需要有固定的地址,因此需要做一个interface的封装,确保传入时候方法已经实例化,而且地址唯一

interface的声明如下

  public static interface ICallback{
        void onCallback(String k, float p);
    };

接著在registerCallback声明的地方将interface做为入参

public static native int registerCallback(IProgressCallback callBack);

到此为止,回调函数的封装已经全数完成了,可以在业务层实现一个简单的回调函数测试一下

testSdk.registerCallback(new testSdk.ICallback() {
            @Override
            public void onCallback(String k, float p) {
                Log.e("[onUpdateCallback][" + k +  "] p: " + p);  
                });
            }
        });

3. 使用iOS Swift方法作为回调函数

一般C++跟Swift交互会需要一个Object C的桥接层,而且C++本身跟ObjectC兼容起来相对容易很多,因此建议就从Object C的桥接层著手

3.1 Object C 桥接层声明

// bridge.h

// 定义callback function 类型
#ifdef __cplusplus
extern "C" {
#endif
    typedef void(*callback_t)(const char * k, float p);
#ifdef __cplusplus
} // extern "C"
#endif

// 注册回调函数
- (int) registCallback: (callback_t) callback;

根据C++ 声明的回调函数声明一个type,并将这个type当成注册函数registCallback的入参

3.2 Object C注册函数的实现

注册函数的实现相对于Android相对单纯,直接调用C++层注册函数即可

// bridge.mm

- (int) setProgressCallback: (callback_t) callback{
    // 调用C++层注册函数
    int ret = test_sdk->setCallback(callback);
    return ret;
}

3.3 Swift层实现回调函数

以下是在Swift中实现一个简单的回调函数示例

test_sdk.setPCallback({(value1, value2) -> Void in
            if let pointer = value1 {
                let swiftString = String(cString: pointer)
                print("p[\(swiftString)]:", value2);
            } else {
                print("CallBack Return None")
            }    
        })

结论

本文章将两种不同平台的方法传入C++层做为回调函数的实现,其实可以发现C++层是不需要进行更动的,主要是需要在桥接层进行函数转换,安卓的部分在JNI实现,iOS则是在ObjectC Bridge实现。目的其实也是相同的,就是将一个Java/Swift方法实例化,并且具有固定的调用地址,提供给C++层进行调用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C语言中,传感器回调函数通常用于接收传感器数据并进行处理。传感器回调函数是一个普通的函数,可以通过函数指针传递给其他函数,在需要的时候被调用。传感器回调函数的定义和使用方法如下所示: 首先,在定义传感器类时,需要定义一个静态成员函数作为回调函数回调函数的定义方式可以参考以下示例代码: ```c class Sensor{ public: Sensor(){} ~Sensor(){} // 定义回调函数onHeight static void onHeight(double height, void* context) { cout << "current height is " << height << endl; } // 定义注册回调函数 void registCallback() { registHeightCallback(onHeight, this); } }; ``` 然后,在需要使用传感器回调函数的地方,可以通过函数指针的方式将回调函数传递给其他函数,以便在合适的时机调用该回调函数。具体的使用方式可以参考以下示例代码: ```c // 主函数 int main() { Sensor sens; sens.registCallback(); // 其他函数中调用回调函数的地方 // ... return 0; } ``` 需要注意的是,传感器回调函数通常是静态成员函数,因为静态成员函数可以直接访问非静态成员函数。这样可以确保回调函数能够正确地操作传感器类的实例。 总之,在C语言中,传感器回调函数是用于接收传感器数据并进行处理的普通函数。通过函数指针的方式将回调函数传递给其他函数,在适当的时机调用该回调函数。<span class="em">1</span><span class="em">2</span><span class="em">3</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值