【发布时间】:2021-10-19 10:24:09
【问题描述】:
主要目标是在 Android Studio 中开发一个带有基于 qt 的共享库 (.so) 的 .apk,并在其中启动隐藏的 qt 事件循环 (QCoreApplication)。
首先,
我担心如何在 QtCreator 中制作一个 Android 存档 (.aar) 以便在任何目标 .apk 中使用它 - 但这是错误的方式,因为 QtCreator 不支持 Android 存档。顺便说一下,要创建一个.aar 而不是.apk,我们应该在cmake 构建目录中更改build.gradle,如图所示
但现在无论我们从哪里获得.so 将它们带入 Android Studio 项目。
Android Studio 项目
然后我们应该创建一个基于活动的AndroidStudio 项目,然后创建一个带有本地方法initSdk 的SdkService 类,应该调用它来启动我们隐藏的本地处理。这里是提到的类的路径:AndroidStudioProjects/MyApplication7/app/src/main/java/io/company/companySdk/SdkService.java
AndroidStudio 项目概览
JNI 标头
然后生成我使用javac的JNI头:
$ pwd
/home/rozhkov/AndroidStudioProjects/MyApplication7/app/src/main/java/io/company/companySdk
$ javac -h . SdkService.java
这是initSdk 的定义,将其包含在io_company_companySdk_SdkService.h 的qtcreator 项目中:
extern "C"
JNIEXPORT void JNICALL Java_io_company_companySdk_SdkService_initSdk
(JNIEnv *, jclass);
QtCreator 项目
我创建了 Qt Widgets Application,因为 qtcreator 会为此类项目生成 androiddeployqt 步骤。 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()
main.cpp
#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;
}
部署到 Android Studio
Qtcreator 的构建结果是 cmake 构建目录中的android-build-debug.apk。从那里我们应该将所有相关的.so 复制到jniLibs 特殊目录中,共享库应该作为目标.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 调用原生代码
下一步是从 Java 加载共享库并调用提到的initSdk。我通过以下方式扩展了MainActivity 类:
从 java 调用本地人
运行 Android 应用程序 - SEGFAULT
最后我们的 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 更适合这种任务。
或者可能是其他任何东西......
有人知道应该怎么做吗?
【问题讨论】:
标签: android-studio qt java-native-interface aar