编译生成.so文件

本文详细介绍如何在Android工程中引入C++支持,包括构建.so文件、动态注册JNI方法、获取方法签名等关键步骤,适合希望深入了解跨语言编程的开发者。

1.新建带c++支持的Android工程。demo编译运行无误

2.在CMakeLists.txt中添加,生成.so文件可以拷贝出去供其他工程使用

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#设置生成的so动态库最后输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})

#在add_library之前设置
add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             native-lib.cpp )

3.动态注册native方法名称,这样可以注册其他工程的包路径

采用RegisterNatives的注册jni的方法不需要javah命名生成的jni规则函数的命名方式

RegisterNatives为JNIEnv的成员函数,声明为:
    jint (JNICALL *RegisterNatives) (JNIEnv *env, jclass clazz, const JNINativeMethod *methods,jint nMethods);

其对应的取消注册的函数为声明为:
    jint (JNICALL *UnregisterNatives) (JNIEnv *env, jclass clazz);

在java中调用System.loadLibrary("somelib");的时候,系统会自动调用jni的函数JNI_OnLoad,
在程序退出的时候,系统卸载“somelib”,会自动调用jni的函数JNI_OnUnload,
所以我们需要在jni的接口文件中重写这两个函数

 

 

例子如下:

// native方法
static jstring JNICALL native_get_String(JNIEnv *env, jobject obj, jobject content) {

}

//先定义一个字符串,内容为类名
const char* JNIT_CLASS = "com/mobile/encrypt/api/whitebox/EncryptUtils";

/**
 * 注册的native调用的方法
 */
static const JNINativeMethod gMethods[] = {
        {"stringFromJNI",  "(Landroid/content/Context;)Ljava/lang/String;", (jstring *) native_get_String}
};

/**
 * 注册方法到JVM中
 * System.loadLibrary会自动调用JNI_OnLoad,在此进行动态注册
 *
 * @param jvm
 * @param reserved
 * @return
 */
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
    // 防止动态调试
    ptrace(PTRACE_TRACEME, 0, 0, 0);

    JNIEnv *env = NULL;//注册时在JNIEnv中实现的,所以必须首先获取它
    jint result = JNI_FALSE;

    //获取env指针
    if (jvm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {//从JavaVM获取JNIEnv,使用1.6的版本
        return result;
    }

    //获取类引用,写类的全路径(包名+类名)
    jclass clazz = env->FindClass(JNIT_CLASS);
    if (clazz == NULL) {
        return result;
    }

    //注册方法
    if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
        return result;
    }
    //成功
    result = JNI_VERSION_1_6;
    return result;
}

JNIEXPORT void JNICALL
JNI_OnUnLoad(JavaVM *jvm, void *reserved){
    JNIEnv *env = NULL;
    //获取env指针
    if (jvm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {//从JavaVM获取JNIEnv,使用1.6的版本
        return result;
    }

    //获取类引用,写类的全路径(包名+类名)
    jclass clazz = env->FindClass(JNIT_CLASS);
    if (clazz == NULL) {
        return result;
    }
    jint nRes = env->UnregisterNatives(cls clazz);
    return;
}

说明:

定义的JNIT_CLASS表示的是要调用的jni的java类的名称

gMethods[]为RegisterNatives的第三个函数,表示的是所有jni的函数的集合。

JNINativeMethod是表示jni的方法的结构体,具体结构如下

typedef struct{
    char *name;
    char *signature;
    void *fnPtr;
} JNINativeMethod;

name:为Java类中的native函数的名称
signature:为Java类中的native函数的Java类型描述
fnPtr为jni中对应的函数名称,格式为类似(void*)MethodName

函数和变量的java类型描述可以通过命令 javap -s -p classname来获得,classname与使用javah时写的名称一致,javah生成的头文件的每个函数的注释中也有这个描述

对于“()Ljava/lang/String;”一个描述,表示该函数没有参数,返回值为String。括号内的是参数列表,后面跟的是返回值

java中简单类型和jni中的描述的对应关系如下表所示:

Field Descriptor	Java Language Type
Z	                boolean
B	                byte
C	                char
S	                short
I	                int
J	                long
F	                float
D	                double
 

对于复杂类型,字符串描述以“L”开头,以“;”结束,例如java中的 String ,在jni中的描述为"Ljava/lang/String;"

对于数组,以“[”开头,接类型描述,例如int[ ],在jni中的描述为“[I”;String[ ], 对应为“[Ljava/lang/String;”;如果是数组维数增加一维,则"["增加一个,例如int[ ][ ],对应为“[[I”;

4.获取方法签名,在build的 /build/intermediates/classes/debug/目录下获取定义native方法名称工具类的class文件。使用javap -s  xxx.class获取方法签名

5.CMakeLists.txt添加多c++源文件

6.gradle配置

 

 

 

<think>好的,我现在需要帮助用户解决如何使用Qt和MinGW编译生成.so文件的问题。首先,我得回忆一下Qt编译共享库的步骤,并结合用户提供的引用内容来确保答案的准确性。 用户提到使用MinGW,但引用1和2中提到MinGW在Windows下适用,而生成.so文件通常是Linux的,不过Qt的MinGW可能支持跨平台编译。需要注意用户是否在Windows下交叉编译Linux的.so,或者是否在Linux环境下使用MinGW。不过通常,在Linux下更常用GCC,而MinGW多用于Windows。可能用户的环境是Windows下的Qt配置MinGW来生成Linux的共享库,但这种情况可能需要交叉编译工具链。或者用户可能混淆了环境,需要进一步澄清,但用户的问题明确提到生成.so,这是Linux的共享库,所以可能需要指导在Linux下使用Qt和GCC,或者指导在Windows下使用MinGW生成.dll,但用户明确需要.so,所以假设用户是在Linux环境下使用Qt,并且可能使用MinGW作为编译器?这里可能存在矛盾,因为通常Linux下使用GCC,而MinGW是用于Windows的GNU工具链。可能用户有误解,或者希望用Qt的MinGW配置来生成.so。需要确认这一点。 根据引用2,编译成功后生成文件夹名称包含“MinGW”,可能用户是在Windows下使用Qt的MinGW编译器,但生成的是Linux的库,这可能需要交叉编译配置。或者用户是在Linux下使用MinGW?这不太常见。可能用户的环境是Windows,但想生成Linux的.so文件,这需要交叉编译。或者,用户可能混淆了.dll和.so,实际想生成的是动态库,但环境是Windows下的MinGW,生成.dll。但问题明确要求生成.so,所以可能需要指导在Linux下的步骤。 根据引用3中的示例代码,生成共享库需要创建项目,并正确配置.pro文件。引用4提到Linux下生成.so文件结构,说明用户可能关注的是Linux环境。因此,我需要整理在Linux环境下使用Qt Creator和MinGW(或更可能的GCC)来生成.so文件的步骤,或者可能用户是在Windows下使用Qt的MinGW,但生成的是.dll,但用户的问题明确提到.so,所以可能需要指出环境的不同。 可能用户存在误解,需要明确说明:.so是Linux的共享库,而MinGW通常在Windows下生成.dll。如果用户希望在Windows下生成.so,可能需要使用MXE(M Cross Environment)进行交叉编译,但这可能比较复杂。或者用户可能是在Linux环境下使用Qt和GCC,而误将编译器称为MinGW。需要仔细处理这种情况。 根据引用1中提到“windows下使用MinGW也可以”,可能用户是在Windows下,但想生成Linux的.so,这需要交叉编译。但通常这种情况下,用户需要设置交叉编译工具链,这可能比较复杂。或者用户可能只是想在Linux下使用Qt和GCC编译生成.so,而问题中的MinGW是笔误。需要确认,但根据用户的问题,可能应该以Linux环境为例,使用Qt和GCC编译器,但引用中提到了MinGW,所以可能需要兼顾两种情况。 现在需要综合引用中的信息。引用1提到在Linux下编译qtbase目录,并需要安装xcb/opengl等依赖。引用2说明编译成功后的目录结构,包含MinGW字样,这可能是在Windows下使用MinGW编译生成的是Windows下的库,但用户需要的是.so,所以可能需要指导Linux下的步骤。 可能的解决步骤: 1. 创建Qt共享库项目:在Qt Creator中选择Library项目,正确配置.pro文件。 2. 配置.pro文件:确保TEMPLATE = lib,CONFIG += shared,正确设置DESTDIR等。 3. 处理导出符号:使用Q_DECL_EXPORT和Q_DECL_IMPORT宏。 4. 构建和生成:使用Qt Creator构建,生成.so文件和相关符号链接。 同时,引用4提到生成四个文件,需要解释软链接的作用。 需要确保步骤适用于Linux环境,可能用户的环境是Linux,所以应该以GCC为例,但问题中提到MinGW,可能需要指出在Windows下生成.so需要交叉编译,通常建议在Linux下使用GCC。 此外,用户提供的引用3中的示例代码展示了如何调用共享库,可能需要说明如何编写测试程序并链接生成.so。 最后,生成相关问题需要考虑用户可能的后续疑问,如版本控制、软链接、依赖处理等。</think>### 如何使用Qt和MinGW编译生成.so文件(Linux共享库) #### 一、环境准备 1. **确认操作系统**:`.so`文件是Linux系统下的动态库格式。若需在Windows环境下生成,需通过**MXE(M Cross Environment)**进行交叉编译,但建议直接在Linux系统中操作更为便捷[^1]。 2. **安装Qt和编译器**: - Linux环境下安装Qt Creator及GCC编译器套件。 - Windows环境下若需生成`.so`,需配置MXE工具链[^2]。 #### 二、创建Qt共享库项目 1. **新建项目**: - 选择`Library` -> `C++ Library`,勾选`Shared Library`选项。 - 输入项目名称(如`MySharedLib`)并选择存储路径。 2. **配置.pro文件**: 确保`.pro`文件包含以下关键配置: ```qmake TEMPLATE = lib # 指定生成文件 CONFIG += shared # 声明为共享库 DESTDIR = $$PWD/../lib # 指定输出目录(可选) ``` #### 三、编写库代码与符号导出 1. **头文件声明**: 在头文件中使用`Q_DECL_EXPORT`宏导出符号: ```cpp // sharedlib.h #include <QtCore/QtGlobal> #if defined(MYSHAREDLIB_LIBRARY) #define MYSHAREDLIB_EXPORT Q_DECL_EXPORT #else #define MYSHAREDLIB_EXPORT Q_DECL_IMPORT #endif class MYSHAREDLIB_EXPORT SharedLib { public: int add(int a, int b); }; extern "C" MYSHAREDLIB_EXPORT int subtract(int a, int b); ``` 2. **源文件实现**: ```cpp // sharedlib.cpp #include "sharedlib.h" int SharedLib::add(int a, int b) { return a + b; } int subtract(int a, int b) { return a - b; } ``` #### 四、构建与生成 1. **选择构建工具链**: - Linux环境下选择GCC编译器。 - Windows下若需交叉编译,需配置MXE的Qt工具链[^2]。 2. **执行构建**: - 点击Qt Creator的`构建`按钮,生成文件位于`build-{projectName}-...`目录[^2]。 - Linux下生成四个文件:`.so`(主链接)、`.so.1`(版本主号)、`.so.1.0`(次版本号)、`.so.1.0.0`(实际库文件)[^4]。 #### 五、测试共享库 1. **编写测试程序**: ```cpp #include <QCoreApplication> #include <qDebug> #include "sharedlib.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); SharedLib lib; qDebug() << lib.add(2, 3); // 输出5 qDebug() << subtract(5, 2); // 输出3 return a.exec(); } ``` 2. **配置链接路径**: 在测试项目的`.pro`文件中添加库路径和链接选项: ```qmake LIBS += -L$$PWD/../lib -lMySharedLib INCLUDEPATH += $$PWD/../include # 若头文件在独立目录 ``` --- ### 关键问题说明 1. **符号导出与版本控制**: - `Q_DECL_EXPORT`确保函数可见性,避免运行时加载失败。 - `.so`的软链接(如`.so.1`)用于管理版本兼容性,更新库时只需修改链接指向[^4]。 2. **依赖处理**: - 编译前需安装`xcb`、`opengl`等依赖库,避免链接错误[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MatrixData

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值