创建shared library

本文介绍如何使用Qt创建共享库(DLL),并通过实例演示如何将共享库集成到其他项目中。文章详细解释了共享库的优点及如何在Qt Creator中设置工程以创建共享库。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前段时间说了Qt一些类库的使用,今天来换一下口味,来看一下程序设计的问题。今天来说的是关于共享库 shared library。

如果你打开一些 Windows 应用程序的目录,你会发现有很多程序的 exe 文件都很小,大约几百K 的样子,并且目录中不仅仅只有一个 exe 文件,还包含着一大堆 dll 文件。这些 dll 其实就是一些共享库,所谓共享库,其实就是一些动态链接库,能够由程序在运行时进行动态加载的库。既然说是共享,那就是说,这些库不仅仅自己的程序可以使 用,并且其他程序也可以使用,例如某些通用算法。如果你发布一下自己编写的 Qt 程序,也会看到很多系统的共享库,就是那些 QtGui.dll 之类的东西。或许你会说,我写的程序没有同其他应用共享的库,就不需要这些了吧!其实不然。因为共享库的一个好处是可以动态加载,也就是说,如果你需要升 级程序,那么就要简单的替换掉这个 dll 就好了,不需要要求用户重新安装全部文件。当然,这些 dll 也是有缺点的:动态加载的东西肯定会比静态编译的东西效率低一些。不过在现在的硬件环境下,这点性能损失已经可以忽略不计了。

今天我们要说的就是如何用 Qt 创建共享库代码。

我们还是使用 QtCreator。在创建工程的时候,我们选择下面的 C++ Library 一项,然后点击 OK。

在接下来的对话框中,有一个下拉列表,分别是 Shared Library(共享库),Statically Linked Library(静态链接库)和 Qt 4 Plugin(Qt 4 插件)。我们选择第一个共享库,后面的步骤中会要求选择加入哪几个 Qt 模块,和前面一样,选择自己需要的部分,最后完成工程的创建。

我们会看到 QtCreator 已经帮我们创建好了一些文件。其中有一个 {projectName}_global.h 的文件是 QtCreator 替我们创建的。下面我们就从这个 {projectName}_global.h 开始:

 
  1. #ifndef LIB_GLOBAL_H  
  2. #define LIB_GLOBAL_H  
  3.  
  4. #include <QtCore/qglobal.h>  
  5.  
  6. #if defined(LIB_LIBRARY)  
  7. #  define LIBSHARED_EXPORT Q_DECL_EXPORT  
  8. #else  
  9. #  define LIBSHARED_EXPORT Q_DECL_IMPORT  
  10. #endif  
  11.  
  12. #endif // LIB_GLOBAL_H  

这个文件中只是定义了两个宏 LIBSHARED_EXPORT,注意这里的 LIB 就是我的工程名字。如果定义了 LIB_LIBRARY,LIBSHARED_EXPORT 定义为 Q_DECL_EXPORT,否则定义为 Q_DECL_IMPORT。看这个名字,就知道这就是把对象导出的语句了。下面我们来编写一个窗口(如果你希望这么做,不要忘记在创建工程时勾选 QtGui 模块,默认是不勾选的):

lib.h

 
  1. #ifndef LIB_H  
  2. #define LIB_H  
  3.  
  4. #include <QMainWindow>  
  5.  
  6. #include "lib_global.h"  
  7.  
  8. class  LIBSHARED_EXPORT MainWindow :  public  QMainWindow {  
  9. public :  
  10.     MainWindow(QWidget *parent = 0);  
  11. };  
  12.  
  13. #endif // LIB_H  

lib.cpp

 
  1. #include "lib.h"  
  2.  
  3. MainWindow::MainWindow(QWidget *parent)  
  4.     : QMainWindow(parent)  
  5. {  
  6. }  

代码很简单,就是创建一个 MainWindow。同前面的代码唯一不同的是,在头文件中,使用了 LIBSHARED_EXPORT 这个宏。你可以简单的把它理解成,我需要把这个类 MainWindow 导出。所谓导出,就是将其编译成一个 dll 文件之后,其他的类可以使用这个导出类。好了,下面和原来一样,编译一下这个工程。在 debug 文件夹下你得到的是一个 lib.dll 文件和 liblib.a。后者是 Linux 下使用的库,这里不再详述。

好了,我们要去使用这个 dll 了。新建另外一个工程,需要吧 .pro 文件修改一下:

 
  1. TARGET = test  
  2. TEMPLATE = app  
  3.  
  4. SOURCES += main.cpp  
  5.  
  6. INCLUDEPATH += ../  
  7.  
  8. LIBS += ../debug/lib.dll 

首先,我们添加了 INCLUDEPATH 这一行。这一行就是为了让我们的 test 项目可以找到 lib.h 和 lib_global.h 这两个文件,你需要把这里的路径替换成符合你的工程的路径。LIBS 这一行则需要告诉编译器(注意,这里是编译器! )到哪里去找到这个 dll 文件。然后我们编写 main.cpp:

 
  1. #include <QtGui/QApplication>  
  2. #include "lib.h"  
  3.  
  4. int  main( int  argc,  char  *argv[])  
  5. {  
  6.     QApplication a(argc, argv);  
  7.     MainWindow w;  
  8.     w.show();  
  9.      return  a.exec();  
  10. }  

注意,我们使用了 lib.h,但是这个文件并没有在 HEADERS 里面声明,Qt 实际上就是从 INCLUDEPATH 这里去找到这个文件。MainWindow 在新建的 test 工程中并没有声明,那么它在哪里呢?当然就是在我们编译出来的 lib.dll 里面啦!我们知道,在链接的时候编译器需要找到实现入口,也就是必须定位到这个 dll,这就是由这个 LIBS 指定的地方。

最后编译运行一下这个 exe 文件,怎么样?哦,如果你照我说的做了的话,你应该得到一个错误:找不到 lib.dll。怎么会找不到呢?不是使用 LIBS 指定了吗?请注意,我们强调了,这个指定是编译期的。dll 是动态链接库,也就是说,在 exe 运行的时候需要找到这个库。运行时查找的顺序是:当前路径 -> 系统路径(通常是 system32)。所以,要把我们先前生成的这个 lib.dll 复制到 exe 所在目录,然后直接双击一下这个 exe 文件。一个窗口出来了!有什么区别吗?运行起来是没有区别的,但是我们知道,这个窗口是在这个 dll 里面实现的!我们想往窗口里面加个按钮?没问题,那就加吧!加完之后重新编译一个新的 dll,复制到 exe 文件夹覆盖旧的,修改就完成啦!我们不需要修改这个 exe 了。

这个时候我们再来回忆一下,我们使用自己创建的 dll 的时候,是不是就和使用 QtGui.dll 一样呢?只不过QtGui.dll 已经放在了库目录下, 不需要手动修改 .pro 文件添加 INCLUDEPATH 和 LIBS 罢了。

本文出自 “豆子空间 ” 博客,请务必保留此出处http://devbean.blog.51cto.com/448512/319479

<think>好的,我现在需要回答用户关于在Android中使用共享库或生成包含共享库的APK的问题。首先,我得回忆一下Android中共享库的基本概念和使用方式。根据用户提供的引用内容,特别是引用[1]提到在Android中使用Qt作为共享系统库,这可能涉及到如何将共享库打包到APK中,或者在多个应用之间共享库以减少体积。 接下来,用户的问题具体分为两部分:使用共享库和创建包含共享库的APK。我需要确保覆盖这两个方面,并且步骤清晰。同时,用户提供的引用[3]提到Android.mk转变成Android.bp,以及模块化设置带来的依赖复杂性,这可能意味着在构建过程中需要注意新的构建系统,比如使用Android.bp文件来配置共享库。 首先,共享库在Android中通常是指动态库(.so文件),可以通过NDK编译生成。开发者需要将库文件放置在jniLibs目录下,然后在build.gradle中配置sourceSets,确保库文件被正确打包进APK。这可能涉及到ABI过滤,如armeabi-v7a、arm64-v8a等。这部分需要详细说明步骤,并给出示例代码。 然后,如果用户想创建自己的共享库,可能需要使用Android Studio的C++支持,创建CMakeLists.txt或Android.mk/Android.bp文件来编译库。引用[3]提到模块化的问题,所以可能需要将库作为独立模块,并在主模块中依赖它。这里需要解释如何配置构建脚本,以及如何处理模块间的依赖关系。 另外,引用[2]提到库项目不能单独编译成APK,必须被其他项目使用。因此,在创建共享库时,需要确保它被应用模块正确引用。这可能涉及到在settings.gradle中包含库模块,并在build.gradle中添加依赖。 需要注意的是,动态库在APK中的位置,以及如何加载它们。System.loadLibrary()方法用于加载库,但需要确保库的名称正确,并且没有lib前缀。此外,如果多个应用使用同一共享库,可能需要考虑将其预装到系统分区,但这需要系统权限,普通应用可能无法做到,因此更常见的做法是将库打包到每个APK中。 可能还需要提到优化策略,比如仅包含必要的ABI以减少APK体积,或者使用Android App Bundle让Google Play根据设备分发合适的库文件。此外,需要注意ProGuard或R8在混淆时可能影响本地方法,需在proguard-rules.pro中保留JNI方法。 总结下来,回答的结构应该是: 1. 使用现有共享库:配置jniLibs,build.gradle设置,加载库代码示例。 2. 创建自己的共享库:创建库模块,配置构建脚本,处理依赖。 3. 注意事项:ABI过滤,动态加载,优化策略,混淆处理。 同时,根据用户提供的引用,尤其是关于Android.bp和模块化的变化,需要提到使用新的构建系统,可能涉及Android.bp文件的配置示例,以及如何处理模块间的复杂依赖关系。 需要确保步骤具体,代码示例正确,例如build.gradle的配置和加载库的Java代码。可能还需要指出常见问题,如库文件位置错误导致无法加载,或ABI不匹配引发的崩溃。 最后,生成相关问题时要围绕Android共享库的使用场景、优化方法、不同构建系统的差异、处理依赖的策略等,确保问题具有针对性和扩展性。</think>在Android中使用共享库或生成包含共享库的APK,需遵循以下步骤: --- ### 一、使用现有共享库 1. **放置共享库文件** 将编译好的`.so`文件(动态库)放入`app/src/main/jniLibs/{ABI}`目录中,如`arm64-v8a`或`x86_64`。若无该目录需手动创建。 *示例结构*: ``` jniLibs/ ├── arm64-v8a/ │ └── libmylib.so └── armeabi-v7a/ └── libmylib.so ``` 2. **配置`build.gradle`** 在模块的`build.gradle`中指定ABI过滤(可选,用于减少APK体积): ```gradle android { defaultConfig { ndk { abiFilters 'arm64-v8a', 'armeabi-v7a' // 仅包含指定架构 } } } ``` 3. **加载共享库** 在Java/Kotlin代码中使用`System.loadLibrary`加载库(无需`lib`前缀): ```java static { System.loadLibrary("mylib"); // 对应libmylib.so } ``` --- ### 二、创建自定义共享库 1. **通过NDK编译生成** - 使用CMake或ndk-build编译C/C++代码为`.so`文件。 - *CMake示例*: ```cmake add_library(mylib SHARED mylib.cpp) ``` 2. **模块化设计(Android Studio项目)** - **创建Android Library模块**: 在Android Studio中新建模块,选择`Native C++ Library`模板。 - **依赖关系**: 在主模块的`build.gradle`中添加: ```gradle dependencies { implementation project(':mylibrary') // 假设库模块名为mylibrary } ``` 3. **Android.bp配置(AOSP环境)** 若在AOSP中开发,需编写`Android.bp`文件定义共享库: ```text cc_library_shared { name: "libmylib", srcs: ["mylib.cpp"], shared_libs: ["liblog"], // 依赖其他库 } ``` --- ### 三、注意事项 1. **ABI兼容性** 确保支持的CPU架构与目标设备匹配,避免`java.lang.UnsatisfiedLinkError`[^3]。 2. **动态加载优化** 使用`ReLinker`等工具增强库加载稳定性,防止安装后库文件损坏。 3. **减小APK体积** - 启用Android App Bundle(AAB),按设备分发特定ABI的库[^1]。 - 在`build.gradle`中配置`split`: ```gradle android { splits { abi { enable true reset() include 'arm64-v8a', 'armeabi-v7a' universalApk false } } } ``` 4. **混淆配置** 在`proguard-rules.pro`中保留JNI方法: ```proguard -keepclasseswithmembernames class * { native <methods>; } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值