C++ SDK fournit une interface de fonction de rappel pour l'intégration Android/iOS

La façon dont C++ fournit une interface de fonction de rappel multiplateforme est une exigence très courante pour le développement commercial, mais en fait, les informations publiques sont très désordonnées et pas nécessairement correctes, donc dans cet article, en prenant Android Java et iOS Swift comme exemples, le système Explique comment créer une interface de fonction de rappel C++ multiplateforme.

Pour C++, la méthode la plus couramment utilisée pour construire des fonctions de rappel est basée sur std :: function

std::functionIl s'agit d'une classe de modèle dans la bibliothèque standard C++, qui est utilisée pour encapsuler des objets de fonction (Function Object), des pointeurs de fonction et des appels (tels que des expressions lambda). Il fournit un moyen générique de stocker, transmettre et appeler différents types d'objets appelables.

std::functionLa fonction principale de consiste à traiter l'objet appelable comme un type de fonction et à fournir un ensemble de fonctions membres pour exploiter et appeler cet objet fonction. Il est déclaré comme suit :

template <typename Signature>
class function;

Signatureest la signature du type de fonction, y compris le type de retour et les types de paramètres.

std::functionL'utilisation de est très flexible et peut être utilisée pour stocker divers types d'objets appelables, notamment des pointeurs de fonction, des pointeurs de fonction membre, des objets de fonction et des expressions lambda. Les objets peuvent être créés par 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::functionIl peut également être utilisé comme paramètre de fonction ou valeur de retour pour obtenir une interface de fonction plus flexible.

Dans l'ensemble, std::functionune méthode d'encapsulation générale est fournie, de sorte que divers objets appelables peuvent être stockés, transférés et appelés de manière unifiée, ce qui améliore la flexibilité et la réutilisabilité du code. Il est très utile dans des scénarios tels que les rappels de fonctions et la gestion des événements.

toute l'idée

Par conséquent, la création d'une interface de fonction de rappel multiplateforme est également développée autour de std :: function , qui est principalement divisé en deux étapes

1. Utilisez std :: function pour créer une interface de fonction de rappel C++ et le code déclenché par la fonction de rappel 

2. Utilisez le langage de la plate-forme cible pour implémenter la conversion de méthode et convertissez la méthode du langage de la plate-forme cible en std :: function Cet article présentera le code pour implémenter la conversion à l'aide d'Android Java et d'iOS Swift

1. Interface de la fonction de rappel C++

Déclarez d'abord une fonction de rappel en C++

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

Ensuite, créez une fonction de rappel pour enregistrer l'interface

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

Enfin, ajoutez le code qui déclenche le rappel au code

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

2. Utilisez la méthode Android Java comme fonction de rappel

2.1 Enregistrement de la méthode de rappel transmis par Java

Il est nécessaire de construire un pont entre les fonctions Java et C++ au niveau de la couche jni. Tout d'abord, nous construisons une interface d'enregistrement de fonction de rappel 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;
}

Il est divisé en plusieurs étapes. La première consiste à construire une référence pour la fonction côté Android. Cette référence est une adresse commune entre Java et C++, où le rappel est l'implémentation de rappel transmise depuis la couche Java.

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

L'étape suivante consiste à trouver l'identifiant de la méthode spécifique. Ici, le nom et le type des paramètres d'entrée et de sortie doivent correspondre correctement. Sinon, une erreur d'adresse se produira. Pour des instructions détaillées, veuillez vous référer à ici

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

2.2 La fonction de déclenchement de rappel réelle de la couche jni

Nous avons construit une méthode Java nommée onUpdateCallback ci-dessus, puis nous devons encapsuler cette méthode Java dans une méthode C++ et appeler la méthode Java lors de l'appel de cette méthode C++

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);
    }
}

Trouvez ensuite un endroit approprié dans la couche jni et enregistrez le Java qui a construit le onUpdateCallback en C++.

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

2.3 Encapsulation d'interface de couche Java

Revenez à l'interface qui vient de construire une interface pour enregistrer les fonctions de rappel dans jni

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

L'objet call_back ici est le corps de la fonction de rappel que nous avons implémenté dans la couche Java, mais comme nous avons besoin d'une adresse fixe, nous devons créer un package d'interface pour nous assurer que la méthode a été instanciée lorsqu'elle est transmise et que l'adresse est unique.

La déclaration d'interface est la suivante

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

Utilisez ensuite l'interface comme paramètre d'entrée dans la déclaration registerCallback

public static native int registerCallback(IProgressCallback callBack);

Jusqu'à présent, l'encapsulation de la fonction de rappel est terminée et une simple fonction de rappel peut être implémentée dans la couche métier pour la tester.

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

3. Utilisation des méthodes iOS Swift comme fonctions de rappel

Généralement, l'interaction entre C++ et Swift nécessite une couche de pontage Object C, et il est relativement facile pour C++ lui-même d'être compatible avec ObjectC, il est donc recommandé de commencer par la couche de pontage Object C

3.1 Déclaration de la couche pont de l'objet 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;

Déclarez un type selon la fonction de rappel déclarée par C++ et utilisez ce type comme paramètre d'entrée de la fonction d'enregistrement registCallback

3.2 Mise en œuvre de la fonction d'enregistrement de l'objet C

L'implémentation de la fonction d'enregistrement est relativement simple par rapport à Android, il suffit d'appeler directement la fonction d'enregistrement de la couche C++

// bridge.mm

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

3.3 La couche Swift implémente la fonction de rappel

Voici un exemple d'implémentation d'une simple fonction de rappel dans 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")
            }    
        })

en conclusion

Dans cet article, les méthodes de deux plates-formes différentes sont transmises à la couche C++ en tant qu'implémentation de la fonction de rappel. En fait, on peut constater que la couche C++ n'a pas besoin d'être modifiée, principalement parce que la conversion de la fonction doit être exécuté dans la couche pont. La partie Android est implémentée dans JNI. iOS est implémenté dans ObjectC Bridge. Le but est en fait le même, qui est d'instancier une méthode Java/Swift avec une adresse d'appel fixe et de la fournir à la couche C++ pour l'appel.

Guess you like

Origin blog.csdn.net/weixin_44491772/article/details/131972045