主要目标是在 Android Studio 中开发一个带有基于 qt 的共享库 (.so) 的 .apk,并在其中启动隐藏的 qt 事件循环 (QCoreApplication)。首先,我担心如何在 QtCreator 中制作 Android 档案 (.aar) 以在任何目标中.apk按原样使用它- 但这是错误的方式,因为 QtCreator 不支持开箱即用的 Android 档案。顺便说一句.aar,.apk我们应该cmake像图片一样在构建目录中更改 build.gradle但是现在无论我们从哪里.so将它们带入 Android Studio 项目。
然后我们应该创建一个基于活动的AndroidStudio项目,然后创建一个SdkService带有本机方法的类,该类initSdk应该被调用以启动我们隐藏的本机处理。这是提到的类的路径:AndroidStudioProjects/MyApplication7/app/src/main/java/io/company/companySdk/SdkService.javaAndroidStudio 项目概览
然后生成我使用的 JNI 标头javac:
$ pwd /home/rozhkov/AndroidStudioProjects/MyApplication7/app/src/main/java/io/company/companySdk $ javac -h . SdkService.java
这是initSdk将其包含在 qtcreator 项目中的定义io_company_companySdk_SdkService.h:
extern "C" JNIEXPORT void JNICALL Java_io_company_companySdk_SdkService_initSdk (JNIEnv *, jclass);
我创建了Qt Widgets Application因为 qtcreatorandroiddeployqt为那种项目生成了步骤。CMAKE 类型的项目对我来说是最常见的。重要的是不要取消与 Widgets qt 库的链接下面是简单的CMakeLists.txt和main.cpp以下几点:
CMakeLists.txt make_minimum_required(VERSION 3.5) project(sample_service VERSION 0.1 LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Widgets REQUIRED) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Widgets REQUIRED) set(PROJECT_SOURCES main.cpp ) if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) qt_add_executable(sample_service MANUAL_FINALIZATION ${PROJECT_SOURCES} ) endif() target_link_libraries(sample_service PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) if(QT_VERSION_MAJOR EQUAL 6) qt_finalize_executable(sample_service) endif()
#include <thread> #include <memory> #include <jni.h> #include <android/log.h> #include <QCoreApplication> #include <QJniEnvironment> extern "C" JNIEXPORT void JNICALL Java_io_company_companySdk_SdkService_initSdk(JNIEnv *, jclass); class ServiceHolder { public: typedef std::unique_ptr<std::thread> UniqueThreadPtr; static void init_app_worker() { if (_appThread) return; _appThread = std::make_unique<std::thread>([]() { int argc = 0; using namespace std::chrono_literals; QCoreApplication app(argc, nullptr); app.exec(); }); } private: static UniqueThreadPtr _appThread; }; ServiceHolder::UniqueThreadPtr ServiceHolder::_appThread; extern "C" JNIEXPORT void JNICALL Java_io_company_companySdk_SdkService_initSdk(JNIEnv * env, jclass) { int argc = 0; __android_log_print(ANDROID_LOG_VERBOSE, "SdkConnect", "Java_io_company_companySdk_SdkService_initSdk"); if (QJniEnvironment::checkAndClearExceptions(env, QJniEnvironment::OutputMode::Verbose)) { __android_log_print(ANDROID_LOG_VERBOSE, "SdkConnect", "Java environment checked"); } else { __android_log_print(ANDROID_LOG_VERBOSE, "SdkConnect", "Java environment not checked"); } ServiceHolder::init_app_worker(); } jint JNI_OnLoad(JavaVM * aVm, void * aReserved) { __android_log_print(ANDROID_LOG_INFO, "SdkConnect", "Company sdk on load"); return JNI_VERSION_1_6; }
Qtcreator 的构建结果是一个android-build-debug.apkin cmake 构建目录。从那里我们应该将所有相关内容复制.so到jniLibs特殊目录中,共享库应该从那里作为 target 的一部分加载.apk。以下说明介绍了如何做到这一点:
$ pwd /home/rozhkov/sources/android/sample_service/build-sample_service-Android_Qt_6_1_2_Clang_x86_64-Release/android-build/build/outputs/apk/debug $ mkdir extracted $ unzip -qod extracted/ android-build-debug.apk $ cp extracted/lib/x86_64/* ~/AndroidStudioProjects/MyApplication7/app/src/main/jniLibs/x86_64/ $ mv ~/AndroidStudioProjects/MyApplication7/app/src/main/jniLibs/x86_64/libsample_service_x86_64.so ~/AndroidStudioProjects/MyApplication7/app/src/main/jniLibs/x86_64/libsample_service.so $ ls ~/AndroidStudioProjects/MyApplication7/app/src/main/jniLibs/x86_64/ libc++_shared.so libplugins_imageformats_qjpeg_x86_64.so libplugins_styles_qandroidstyle_x86_64.so libQt6Network_x86_64.so libplugins_imageformats_qgif_x86_64.so libplugins_networkinformationbackends_androidnetworkinformationbackend_x86_64.so libQt6Core_x86_64.so libQt6Widgets_x86_64.so libplugins_imageformats_qico_x86_64.so libplugins_platforms_qtforandroid_x86_64.so libQt6Gui_x86_64.so libsample_service.so
下一步是从 Java 加载共享库并调用提到的initSdk. 我MainActivity通过以下方式扩展了课程:从java调用本地人
最后,我们的 Android 应用程序示例在 x86_64 模拟器设备上运行。总是在示例.apk运行时libsample_service.so在QCoreApplication构造函数中发生 SEGFAULT 。在堆栈跟踪中,我们可以看到它恰好发生在QJniEnvironmentPrivate:堆栈跟踪不管它是使用 Qt 6.1.2 还是例如 5.1.15 - 段错误总是发生。
还有一些我没有及时做出的选择。例如,这可能是由于未设置应用程序版本而发生的。
#04 pc 000000000032da4a /data/app/rozhkov.example.myapplication-2/lib/x86_64/libQt6Core_x86_64.so (_ZNK23QCoreApplicationPrivate10appVersionEv+372)
或者可能是QtAndroidService更适合那种任务。或者可能是其他任何东西……
有谁知道应该怎么做?