C++ 静态库:实现 wrappers/bridges 中针对本机移动方法的定义(Java、Objective-C)
C++ static library: implement definitions in wrappers/bridges toward native mobile methods (Java, Objective-C)
我有一个 C++ 静态库 (.a
),我想在本机移动开发中使用它。
为此,我必须在 C++ 和本机之间建立桥梁 languages/SDKs :
java
(Android) : JNI
C#
(Windows) : P\Invoke
Objective-C
(iOS) : Objective-C++
模块
现在,假设我的 C++ 静态库中实现的代码严重依赖于这个函数:
const void sendRawData(std::vector<unsigned char> data);
事情是这样的:这个方法没有在我的库中实现,只是定义了。对于前面提到的所有三个目标,都有类似的方法:例如 java
.
中的 void sendData(String data);
是否可以编译 - 例如 - Android 库(.aar
文件),其中静态库方法中的 sendRawData
符号在我的部分实现android-studio
项目,在哪桥接到前面提到的原生类似方法?
关于iOS,我们可以使用xcode
生成一个.framework
库,方法类似。
感谢您的帮助。
此答案正在进行中。
因此,您想采用 C++ 静态库并将其中一个函数(在库的编译中未定义)桥接到本机移动函数 (iOS/Android/Windows Phone)。
首先最重要的问题:
你为什么要这样做?
在我的例子中, 它是代码维护的混合体(让三个团队在三个不同的 code-bases 上工作只是为了添加一个小功能很烦人),以及降低项目成本。假设您正在处理一些高度敏感的代码,您很可能必须对处理逻辑的整个代码进行认证。三个目标意味着三倍的认证。因此,在 C++ 库中开发程序的整个逻辑非常重要,该库将 完全 与其他平台(iOS、Android , Windows Phone), 这意味着完全相同的 CRC32.
我们要怎么做?
我们将在每个平台上完成 walk-through 如何做到这一点。在示例中,我们将声明一个 C++ static void NativePrint(std::string str)
方法,该方法将由名为 NativeAPI
的 C++ 模块调用。此 API 稍后将暴露给移动开发人员(我们将这样做,使用 Djinni)以实际允许他们调用您的通用 C++ 方法。
这可以分为 6 个步骤:
1。编译静态库
CallsNativePrint.h:
#pragma once
#include <cstdint>
class CallsNativePrint
{
public:
static void __attribute__((weak)) NativePrint(int32_t i);
};
NativeAPI.h:
#pragma once
#include "CallsNativePrint.h"
class NativeAPI
{
public:
static void IncrementAndCallNativePrint(int32_t i);
};
NativeAPI.cpp:
#include "NativeAPI.h"
void NativeAPI::IncrementAndCallNativePrint(int32_t i)
{
// You can here do whatever you want with str.
// The purpose of this exercise is to define a logic inside
// of your C++ code, not just to call native methods.
i++;
CallsNativePrint::NativePrint(i);
}
编译:Makefile
.PHONY:all
all:
gcc -std=c++11 -c NativeAPI.cpp -o NativeAPI.o
ar rcs libNativePrint.a NativeAPI.o
完成这一步后,您就可以正确编译本机库了。其中最重要的部分在 CallsNativePrint.h 中:static void __attribute__((weak)) NativePrint(std::string str);
__attribute__((weak))
部分告诉编译器该函数的符号将弱链接,这意味着我们稍后可以用一个强一(默认情况下所有符号都是强的)。
一旦这一步结束,我们可以检查编译后的库并检查我们的函数的符号确实是 weakly-typed:
$ nm libNativePrint.a
NativeAPI.o:
SUCCESS --> w _ZN16CallsNativePrint11NativePrintEi
0000000000000000 T _ZN9NativeAPI27IncrementAndCallNativePrintEi
0000000000000000 r _ZStL19piecewise_construct
对于接下来的步骤,我们将把它分成 Android 和 iOS,从 Android 开始。
Android
2。创建 Android Studio 项目
为了限制我们的工作,我们将让 Android Studio 生成项目的整个 back-bone。然后,我们将使用它的构建器工具将我们的 C++ 静态库合并到解决方案中。
继续创建一个新的 Android Studio 项目:
- 姓名:
MyNativeAndroidLibrary
- 公司域名:
overflow.stack.com
- 包括 C++ 支持:选中。
- Select 您的应用 运行 的外形规格:我检查了“Phone 和平板电脑”的情况,“API 15:Android 4.0.3" 虽然我怀疑它的重要性。
- 不要添加任何 activity。
- 我无一例外地选择了C++11工具链或者运行时间类型信息支持
首先,我们将在 android-studio
中使用 cmake
编译我们的 previously-created 源代码。这将使事情变得更容易,因为 android-studio
将自动生成每个需要的架构并将它们放在正确的文件夹中。
让你的项目检查员处于 Android
模式,前往 ExternalBuildFiles/CMakeLists.txt
,删除其中的所有内容,然后添加:
cmake_minimum_required(VERSION 3.4.1)
# Path to our source code
set(LIB_SRC_DIR ${CMAKE_SOURCE_DIR}/../../native-library-cpp)
# Add our c++ static library (android-studio recompiles it)
add_library(native-library-cpp STATIC ${LIB_SRC_DIR}/NativeAPI.cpp)
include_directories(${LIB_SRC_DIR}/)
# Native shared library generated by android-studio: will contain JNI code
add_library(native-lib SHARED src/main/cpp/native-lib.cpp)
include_directories(src/main/cpp/)
# Find native Android logging library: that's what we'll call!
find_library(log-lib log)
# Link the JNI shared library with our static library
target_link_libraries(native-lib
native-library-cpp log-lib)
确保将 LIB_SRC_DIR
路径更改为与您的匹配的路径。我的 Android Studio 项目目录和包含本机 C++ 库的目录在同一目录中。
然后,我们要配置Android Studio 生成一个.aar
文件。转到 app:build.gradle
,并将第一行 (apply plugin: 'com.android.application'
) 更改为 apply plugin: 'com.android.library'
。您还必须删除行 applicationId
,否则构建将失败。
一旦完成,我们就可以开始将 C++ 方法桥接到我们的本机 Java 方法!
3。将 C++ NativePrint
方法桥接到本机 Android 函数
为此,我们将在 Android Studio 自动生成的 native-lib.cpp
文件中编写代码。首先,创建一个包含以下内容的 native-lib.h
文件(通过 right-clicking on native-lib.cpp
):
#pragma once
#include <cstdint>
#include <cstdbool>
#include <string>
#include <android/log.h>
#include <jni.h>
// JNI fields used to link C++ to Java
extern JavaVM *jvm;
extern JNIEnv *env;
extern jclass cls;
extern jmethodID mid;
class CallsNativePrint
{
private:
// JNI initializers
static bool _hasBeenInitialized;
static void InitializeJNIFields();
static bool GetNativeMethod();
public:
static void NativePrint(int32_t i); // We're going to re-define this symbol
// Note that it is already existing in native-library-cpp, as a weak symbol.
};
这个header定义了一些东西:
- 它包括其中使用的任何内容。
- 它声明了一些 extern JNI-related 变量:这实际上被推荐为 JNI 最佳实践。获取对 java classes 或方法的引用需要很长时间。你不想每次打电话都这样做
JNI 方法。获取对 JVM/Java 环境的引用也是如此。
- 它声明了 CallsNativePrint class。在那里,我们 re-declare
static void NativePrint(int32_t i)
方法。一旦定义,它的符号将删除静态库中的那个,它是每次调用IncrementAndCallNativePrint
时都会调用的方法!
现在,在 native-lib.cpp
中,我们必须转储所有这些文档化代码:
#include <jni.h>
#include <string>
#include "native-lib.h"
// JNI-related variables declaration
JavaVM *jvm;
JNIEnv *env;
jclass cls;
jmethodID mid;
bool CallsNativePrint::_hasBeenInitialized = false;
void CallsNativePrint::InitializeJNIFields()
{
env->GetJavaVM(&jvm);
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6;
args.name = NULL;
args.group = NULL;
jvm->AttachCurrentThread(&env, &args);
CallsNativePrint::_hasBeenInitialized = true;
}
// Find a Java method based on class name ("JavaBridge"),
// method name ("PrintInteger"), and JNI prototype string ("(I)V").
bool CallsNativePrint::GetNativeMethod()
{
if (cls || mid)
{
__android_log_write(ANDROID_LOG_INFO, "native-lib",
"Native method has already been recovered!");
return true;
}
cls = env->FindClass("JavaBridge");
if (!cls) {
__android_log_write(ANDROID_LOG_ERROR, "native-lib",
"Couldn't find Java JavaBridge class!");
return false;
}
jmethodID mid = env->GetStaticMethodID(cls, "PrintInteger",
"(I)V");
if (mid == nullptr)
{
__android_log_write(ANDROID_LOG_ERROR, "native-lib",
"Couldn't find Java StaticRuaBridge::SendRawCommandBridge method!");
return false;
}
return true;
}
// Converts int32_t to jint (no conversion needed in this case!)
// then calls the `mid` method recovered in GetNativeMethod().
void CallsNativePrint::NativePrint(int32_t i)
{
if (!_hasBeenInitialized) { InitializeJNIFields(); }
if (GetNativeMethod())
{
jint ji = i;
env->CallStaticVoidMethod(cls, mid, ji); // Call the JavaBridge method!
}
}
完成后,我们必须实际编写从 JNI 调用的 Java 代码。创建一个JavaBridge.java
java class(右击java/
文件夹),然后pu其中:
package com.stack.overflow.mynativeandroidlibrary;
import android.util.Log;
public class JavaBridge {
public static void PrintInteger(int i) {
Log.i("JavaBridge", "PrintInteger: " + i);
}
}
如果正确完成,一旦一切都链接在一起,CallsNativePrint::NativePrint(int32_t i)
现在应该关联到 native-lib.cpp
末尾的代码,这意味着 JNI 代码直接调用 Java 方法。成功!
现在,如果我们希望我们的库有任何用处,我们需要公开一个 Java API,这将允许 Android 开发人员直接调用我们的 C++ 中定义的方法图书馆 ;在我们的例子中,它将是从 Java 到 IncrementAndCallNativePrint(int32_t i)
!
的直接调用
要检查一切是否正常,您可以通过单击 Build -> Build APK
上的顶部栏生成一个 .aar
文件。
4。向 Android 开发人员
公开 C++ 方法
对于这部分,我们将广泛使用Djinni.首先,安装它。
然后,在项目的根目录下创建一个 djinni-generation/
文件夹。在此项目中,创建一个包含以下内容的 api.djinni
文件:
NativeAPIExposer = interface +c {
static IncrementAndCallNativePrint(i: i32);
}
使用djinni
,这将允许我们创建描述此API的C++、Objective-C和Java文件(将在C
, 注意 +c
!).
创建另一个名为 run-djinni.sh
的文件,内容如下:
#! /usr/bin/env /bin/bash
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cpp_out="$base_dir/generated-src/cpp"
jni_out="$base_dir/generated-src/jni"
java_out="$base_dir/generated-src/java/com/stack/overflow"
objc_out="$base_dir/generated-src/objc"
java_package="com.stack.overflow"
namespace="native_api"
djinni="$base_dir/api.djinni"
mkdir ./generated-src/
djinni \
--java-out $java_out \
--java-package $java_package \
--ident-java-field mFooBar \
\
--cpp-out $cpp_out \
--cpp-namespace $namespace \
\
--jni-out $jni_out \
--ident-jni-class NativeFooBar \
--ident-jni-file NativeFooBar \
\
--objc-out $objc_out \
--objc-type-prefix DB \
\
--idl $djinni
完成后,generated-src/
文件夹将像这样填充:
$ tree -pf ./generated-src/
generated-src
├── [drwxrwxr-x] generated-src/cpp
│ └── [-rw-rw-r--] generated-src/cpp/NativeAPIExposer.hpp
├── [drwxrwxr-x] generated-src/java
│ └── [drwxrwxr-x] generated-src/java/com
│ └── [drwxrwxr-x] generated-src/java/com/stack
│ └── [drwxrwxr-x] generated-src/java/com/stack/overflow
│ └── [-rw-rw-r--] generated-src/java/com/stack/overflow/NativeAPIExposer.java
├── [drwxrwxr-x] generated-src/jni
│ ├── [-rw-rw-r--] generated-src/jni/NativeNativeAPIExposer.cpp
│ └── [-rw-rw-r--] generated-src/jni/NativeNativeAPIExposer.hpp
└── [drwxrwxr-x] generated-src/objc
└── [-rw-rw-r--] generated-src/objc/DBNativeAPIExposer.h
对于Android项目,抓取generated-src/cpp/
、generated-src/java/
和generated-src/jni/
中的文件,并将它们导入到Android中的相应文件夹中工作室。
注意:如果找不到jni/
文件夹,请从Android
项目视图切换到Project
项目视图。
我们现在必须实现 NativeAPIExposer
方法。在 cpp/
文件夹中创建一个 NativeAPIExposer.cpp
文件,内容如下:
#include "NativeAPIExposer.hpp"
#include "NativeAPI.h"
void native_api::NativeAPIExposer::IncrementAndCallNativePrint(int32_t i)
{
NativeAPI::IncrementAndCallNativePrint(i); // Call the native method!
}
我们现在必须编译新的库并将它们添加到 CMakeLists.txt 文件中:
# Native API exposer
set(DJINNI_PATH RELATIVE/PATH/TO/DJINNI)
add_library(native-api-lib SHARED
src/main/cpp/NativeAPIExposer.cpp
src/main/jni/NativeNativeAPIExposer.cpp
${DJINNI_PATH}/support-lib/jni/djinni_main.cpp
${DJINNI_PATH}/support-lib/jni/djinni_support.cpp)
include_directories(src/main/cpp/ src/main/jni/ ${DJINNI_PATH}/support-lib/jni/)
编辑以下行:
target_link_libraries(native-lib native-library-cpp log-lib)
收件人:
target_link_libraries(native-lib native-library-cpp log-lib native-api-lib)
5。生成 .aar
库
如前所述,此时您只需点击Build -> Build APK
上的顶部栏即可。
6.在另一个 Android 项目
中使用本机库
首先创建一个新的 Android Studio 项目(有一个空的 activity),然后添加生成的 .aar
作为依赖项。
这样做:
File -> New -> New Module
, « Import .JAR/.AAR Package », 和 select 由 Android Studio 生成的,应该在 "MyNativeAndroidLibrary/app/build/outputs/aar/" .
- 右键单击您的项目 -> 打开模块设置 -> 依赖项 -> 添加模块依赖项 -> 添加我们自己的模块。
在 activity 的代码中,您应该可以使用以下代码访问我们的函数:
iOS
2。创建 Android Studio 项目
占位符
3。将 C++ NativePrint
方法桥接到本机 iOS 函数
占位符
4。将 C++ 方法导出给 iOS 开发人员
对于这部分,我们将大量使用 Djinni.
5。生成 .framework
库
占位符
6.在另一个 iOS 项目
中使用本机库
我有一个 C++ 静态库 (.a
),我想在本机移动开发中使用它。
为此,我必须在 C++ 和本机之间建立桥梁 languages/SDKs :
java
(Android) :JNI
C#
(Windows) :P\Invoke
Objective-C
(iOS) :Objective-C++
模块
现在,假设我的 C++ 静态库中实现的代码严重依赖于这个函数:
const void sendRawData(std::vector<unsigned char> data);
事情是这样的:这个方法没有在我的库中实现,只是定义了。对于前面提到的所有三个目标,都有类似的方法:例如 java
.
void sendData(String data);
是否可以编译 - 例如 - Android 库(.aar
文件),其中静态库方法中的 sendRawData
符号在我的部分实现android-studio
项目,在哪桥接到前面提到的原生类似方法?
关于iOS,我们可以使用xcode
生成一个.framework
库,方法类似。
感谢您的帮助。
此答案正在进行中。
因此,您想采用 C++ 静态库并将其中一个函数(在库的编译中未定义)桥接到本机移动函数 (iOS/Android/Windows Phone)。
首先最重要的问题:
你为什么要这样做?
在我的例子中, 它是代码维护的混合体(让三个团队在三个不同的 code-bases 上工作只是为了添加一个小功能很烦人),以及降低项目成本。假设您正在处理一些高度敏感的代码,您很可能必须对处理逻辑的整个代码进行认证。三个目标意味着三倍的认证。因此,在 C++ 库中开发程序的整个逻辑非常重要,该库将 完全 与其他平台(iOS、Android , Windows Phone), 这意味着完全相同的 CRC32.
我们要怎么做?
我们将在每个平台上完成 walk-through 如何做到这一点。在示例中,我们将声明一个 C++ static void NativePrint(std::string str)
方法,该方法将由名为 NativeAPI
的 C++ 模块调用。此 API 稍后将暴露给移动开发人员(我们将这样做,使用 Djinni)以实际允许他们调用您的通用 C++ 方法。
这可以分为 6 个步骤:
1。编译静态库
CallsNativePrint.h:
#pragma once
#include <cstdint>
class CallsNativePrint
{
public:
static void __attribute__((weak)) NativePrint(int32_t i);
};
NativeAPI.h:
#pragma once
#include "CallsNativePrint.h"
class NativeAPI
{
public:
static void IncrementAndCallNativePrint(int32_t i);
};
NativeAPI.cpp:
#include "NativeAPI.h"
void NativeAPI::IncrementAndCallNativePrint(int32_t i)
{
// You can here do whatever you want with str.
// The purpose of this exercise is to define a logic inside
// of your C++ code, not just to call native methods.
i++;
CallsNativePrint::NativePrint(i);
}
编译:Makefile
.PHONY:all
all:
gcc -std=c++11 -c NativeAPI.cpp -o NativeAPI.o
ar rcs libNativePrint.a NativeAPI.o
完成这一步后,您就可以正确编译本机库了。其中最重要的部分在 CallsNativePrint.h 中:static void __attribute__((weak)) NativePrint(std::string str);
__attribute__((weak))
部分告诉编译器该函数的符号将弱链接,这意味着我们稍后可以用一个强一(默认情况下所有符号都是强的)。
一旦这一步结束,我们可以检查编译后的库并检查我们的函数的符号确实是 weakly-typed:
$ nm libNativePrint.a
NativeAPI.o:
SUCCESS --> w _ZN16CallsNativePrint11NativePrintEi
0000000000000000 T _ZN9NativeAPI27IncrementAndCallNativePrintEi
0000000000000000 r _ZStL19piecewise_construct
对于接下来的步骤,我们将把它分成 Android 和 iOS,从 Android 开始。
Android
2。创建 Android Studio 项目
为了限制我们的工作,我们将让 Android Studio 生成项目的整个 back-bone。然后,我们将使用它的构建器工具将我们的 C++ 静态库合并到解决方案中。
继续创建一个新的 Android Studio 项目:
- 姓名:
MyNativeAndroidLibrary
- 公司域名:
overflow.stack.com
- 包括 C++ 支持:选中。
- Select 您的应用 运行 的外形规格:我检查了“Phone 和平板电脑”的情况,“API 15:Android 4.0.3" 虽然我怀疑它的重要性。
- 不要添加任何 activity。
- 我无一例外地选择了C++11工具链或者运行时间类型信息支持
首先,我们将在 android-studio
中使用 cmake
编译我们的 previously-created 源代码。这将使事情变得更容易,因为 android-studio
将自动生成每个需要的架构并将它们放在正确的文件夹中。
让你的项目检查员处于 Android
模式,前往 ExternalBuildFiles/CMakeLists.txt
,删除其中的所有内容,然后添加:
cmake_minimum_required(VERSION 3.4.1)
# Path to our source code
set(LIB_SRC_DIR ${CMAKE_SOURCE_DIR}/../../native-library-cpp)
# Add our c++ static library (android-studio recompiles it)
add_library(native-library-cpp STATIC ${LIB_SRC_DIR}/NativeAPI.cpp)
include_directories(${LIB_SRC_DIR}/)
# Native shared library generated by android-studio: will contain JNI code
add_library(native-lib SHARED src/main/cpp/native-lib.cpp)
include_directories(src/main/cpp/)
# Find native Android logging library: that's what we'll call!
find_library(log-lib log)
# Link the JNI shared library with our static library
target_link_libraries(native-lib
native-library-cpp log-lib)
确保将 LIB_SRC_DIR
路径更改为与您的匹配的路径。我的 Android Studio 项目目录和包含本机 C++ 库的目录在同一目录中。
然后,我们要配置Android Studio 生成一个.aar
文件。转到 app:build.gradle
,并将第一行 (apply plugin: 'com.android.application'
) 更改为 apply plugin: 'com.android.library'
。您还必须删除行 applicationId
,否则构建将失败。
一旦完成,我们就可以开始将 C++ 方法桥接到我们的本机 Java 方法!
3。将 C++ NativePrint
方法桥接到本机 Android 函数
为此,我们将在 Android Studio 自动生成的 native-lib.cpp
文件中编写代码。首先,创建一个包含以下内容的 native-lib.h
文件(通过 right-clicking on native-lib.cpp
):
#pragma once
#include <cstdint>
#include <cstdbool>
#include <string>
#include <android/log.h>
#include <jni.h>
// JNI fields used to link C++ to Java
extern JavaVM *jvm;
extern JNIEnv *env;
extern jclass cls;
extern jmethodID mid;
class CallsNativePrint
{
private:
// JNI initializers
static bool _hasBeenInitialized;
static void InitializeJNIFields();
static bool GetNativeMethod();
public:
static void NativePrint(int32_t i); // We're going to re-define this symbol
// Note that it is already existing in native-library-cpp, as a weak symbol.
};
这个header定义了一些东西:
- 它包括其中使用的任何内容。
- 它声明了一些 extern JNI-related 变量:这实际上被推荐为 JNI 最佳实践。获取对 java classes 或方法的引用需要很长时间。你不想每次打电话都这样做 JNI 方法。获取对 JVM/Java 环境的引用也是如此。
- 它声明了 CallsNativePrint class。在那里,我们 re-declare
static void NativePrint(int32_t i)
方法。一旦定义,它的符号将删除静态库中的那个,它是每次调用IncrementAndCallNativePrint
时都会调用的方法!
现在,在 native-lib.cpp
中,我们必须转储所有这些文档化代码:
#include <jni.h>
#include <string>
#include "native-lib.h"
// JNI-related variables declaration
JavaVM *jvm;
JNIEnv *env;
jclass cls;
jmethodID mid;
bool CallsNativePrint::_hasBeenInitialized = false;
void CallsNativePrint::InitializeJNIFields()
{
env->GetJavaVM(&jvm);
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6;
args.name = NULL;
args.group = NULL;
jvm->AttachCurrentThread(&env, &args);
CallsNativePrint::_hasBeenInitialized = true;
}
// Find a Java method based on class name ("JavaBridge"),
// method name ("PrintInteger"), and JNI prototype string ("(I)V").
bool CallsNativePrint::GetNativeMethod()
{
if (cls || mid)
{
__android_log_write(ANDROID_LOG_INFO, "native-lib",
"Native method has already been recovered!");
return true;
}
cls = env->FindClass("JavaBridge");
if (!cls) {
__android_log_write(ANDROID_LOG_ERROR, "native-lib",
"Couldn't find Java JavaBridge class!");
return false;
}
jmethodID mid = env->GetStaticMethodID(cls, "PrintInteger",
"(I)V");
if (mid == nullptr)
{
__android_log_write(ANDROID_LOG_ERROR, "native-lib",
"Couldn't find Java StaticRuaBridge::SendRawCommandBridge method!");
return false;
}
return true;
}
// Converts int32_t to jint (no conversion needed in this case!)
// then calls the `mid` method recovered in GetNativeMethod().
void CallsNativePrint::NativePrint(int32_t i)
{
if (!_hasBeenInitialized) { InitializeJNIFields(); }
if (GetNativeMethod())
{
jint ji = i;
env->CallStaticVoidMethod(cls, mid, ji); // Call the JavaBridge method!
}
}
完成后,我们必须实际编写从 JNI 调用的 Java 代码。创建一个JavaBridge.java
java class(右击java/
文件夹),然后pu其中:
package com.stack.overflow.mynativeandroidlibrary;
import android.util.Log;
public class JavaBridge {
public static void PrintInteger(int i) {
Log.i("JavaBridge", "PrintInteger: " + i);
}
}
如果正确完成,一旦一切都链接在一起,CallsNativePrint::NativePrint(int32_t i)
现在应该关联到 native-lib.cpp
末尾的代码,这意味着 JNI 代码直接调用 Java 方法。成功!
现在,如果我们希望我们的库有任何用处,我们需要公开一个 Java API,这将允许 Android 开发人员直接调用我们的 C++ 中定义的方法图书馆 ;在我们的例子中,它将是从 Java 到 IncrementAndCallNativePrint(int32_t i)
!
要检查一切是否正常,您可以通过单击 Build -> Build APK
上的顶部栏生成一个 .aar
文件。
4。向 Android 开发人员
公开 C++ 方法对于这部分,我们将广泛使用Djinni.首先,安装它。
然后,在项目的根目录下创建一个 djinni-generation/
文件夹。在此项目中,创建一个包含以下内容的 api.djinni
文件:
NativeAPIExposer = interface +c {
static IncrementAndCallNativePrint(i: i32);
}
使用djinni
,这将允许我们创建描述此API的C++、Objective-C和Java文件(将在C
, 注意 +c
!).
创建另一个名为 run-djinni.sh
的文件,内容如下:
#! /usr/bin/env /bin/bash
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cpp_out="$base_dir/generated-src/cpp"
jni_out="$base_dir/generated-src/jni"
java_out="$base_dir/generated-src/java/com/stack/overflow"
objc_out="$base_dir/generated-src/objc"
java_package="com.stack.overflow"
namespace="native_api"
djinni="$base_dir/api.djinni"
mkdir ./generated-src/
djinni \
--java-out $java_out \
--java-package $java_package \
--ident-java-field mFooBar \
\
--cpp-out $cpp_out \
--cpp-namespace $namespace \
\
--jni-out $jni_out \
--ident-jni-class NativeFooBar \
--ident-jni-file NativeFooBar \
\
--objc-out $objc_out \
--objc-type-prefix DB \
\
--idl $djinni
完成后,generated-src/
文件夹将像这样填充:
$ tree -pf ./generated-src/
generated-src
├── [drwxrwxr-x] generated-src/cpp
│ └── [-rw-rw-r--] generated-src/cpp/NativeAPIExposer.hpp
├── [drwxrwxr-x] generated-src/java
│ └── [drwxrwxr-x] generated-src/java/com
│ └── [drwxrwxr-x] generated-src/java/com/stack
│ └── [drwxrwxr-x] generated-src/java/com/stack/overflow
│ └── [-rw-rw-r--] generated-src/java/com/stack/overflow/NativeAPIExposer.java
├── [drwxrwxr-x] generated-src/jni
│ ├── [-rw-rw-r--] generated-src/jni/NativeNativeAPIExposer.cpp
│ └── [-rw-rw-r--] generated-src/jni/NativeNativeAPIExposer.hpp
└── [drwxrwxr-x] generated-src/objc
└── [-rw-rw-r--] generated-src/objc/DBNativeAPIExposer.h
对于Android项目,抓取generated-src/cpp/
、generated-src/java/
和generated-src/jni/
中的文件,并将它们导入到Android中的相应文件夹中工作室。
注意:如果找不到jni/
文件夹,请从Android
项目视图切换到Project
项目视图。
我们现在必须实现 NativeAPIExposer
方法。在 cpp/
文件夹中创建一个 NativeAPIExposer.cpp
文件,内容如下:
#include "NativeAPIExposer.hpp"
#include "NativeAPI.h"
void native_api::NativeAPIExposer::IncrementAndCallNativePrint(int32_t i)
{
NativeAPI::IncrementAndCallNativePrint(i); // Call the native method!
}
我们现在必须编译新的库并将它们添加到 CMakeLists.txt 文件中:
# Native API exposer
set(DJINNI_PATH RELATIVE/PATH/TO/DJINNI)
add_library(native-api-lib SHARED
src/main/cpp/NativeAPIExposer.cpp
src/main/jni/NativeNativeAPIExposer.cpp
${DJINNI_PATH}/support-lib/jni/djinni_main.cpp
${DJINNI_PATH}/support-lib/jni/djinni_support.cpp)
include_directories(src/main/cpp/ src/main/jni/ ${DJINNI_PATH}/support-lib/jni/)
编辑以下行:
target_link_libraries(native-lib native-library-cpp log-lib)
收件人:
target_link_libraries(native-lib native-library-cpp log-lib native-api-lib)
5。生成 .aar
库
如前所述,此时您只需点击Build -> Build APK
上的顶部栏即可。
6.在另一个 Android 项目
中使用本机库首先创建一个新的 Android Studio 项目(有一个空的 activity),然后添加生成的 .aar
作为依赖项。
这样做:
File -> New -> New Module
, « Import .JAR/.AAR Package », 和 select 由 Android Studio 生成的,应该在 "MyNativeAndroidLibrary/app/build/outputs/aar/" .- 右键单击您的项目 -> 打开模块设置 -> 依赖项 -> 添加模块依赖项 -> 添加我们自己的模块。
在 activity 的代码中,您应该可以使用以下代码访问我们的函数:
iOS
2。创建 Android Studio 项目
占位符
3。将 C++ NativePrint
方法桥接到本机 iOS 函数
占位符
4。将 C++ 方法导出给 iOS 开发人员
对于这部分,我们将大量使用 Djinni.
5。生成 .framework
库
占位符