ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Qt on Android开发环境搭建与交互实战指南

Qt on Android开发环境搭建与交互实战指南 1. Qt on Android开发环境搭建与基础配置1.1 必备组件安装与版本选择在开始Qt on Android开发前需要准备以下核心组件Qt SDK建议5.15 LTS或6.2版本Android Studio含Android SDKJDK推荐OpenJDK 11NDK通过Android Studio SDK Manager安装版本组合的黄金搭配Qt 5.15.2 Android Studio 2021.3.1 NDK r21e这个组合经过大量项目验证稳定性最佳。新版本Qt 6对Android的支持更完善但部分第三方库的兼容性仍需时间验证。注意避免使用过新的NDK版本某些Qt模块如Qt Multimedia在NDK r23上存在已知问题1.2 环境变量关键配置在~/.bashrc或系统环境变量中添加export ANDROID_SDK_ROOT/path/to/Android/sdk export ANDROID_NDK_ROOT/path/to/Android/sdk/ndk/21.3.6528147 export JAVA_HOME/path/to/jdk-11.0.15 export PATH$JAVA_HOME/bin:$ANDROID_NDK_ROOT:$PATH验证配置是否生效qmake -v javac -version adb devices1.3 Qt Creator中的Android设置打开Qt Creator → 工具 → 选项 → 设备 → Android指定JDK、SDK、NDK路径配置ABI建议armeabi-v7a arm64-v8a创建Android虚拟设备AVD或连接物理设备常见问题排查若遇到Could not find the Qt platform plugin xcb错误检查部署配置中是否包含plugins/platforms/libandroid.so中文乱码问题需在pro文件中添加DEFINES QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII2. Qt与Android原生交互机制2.1 JNI桥梁构建实战Qt通过JNI调用Android API的标准流程在Java中声明native方法public native void qtCallJavaMethod(String msg);在C中实现对应方法extern C JNIEXPORT void JNICALL Java_com_example_MyApp_qtCallJavaMethod(JNIEnv *env, jobject obj, jstring msg) { const char *utfMsg env-GetStringUTFChars(msg, nullptr); qDebug() From Java: utfMsg; env-ReleaseStringUTFChars(msg, utfMsg); }注册JNI方法在JNI_OnLoad中jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; if (vm-GetEnv(reinterpret_castvoid**(env), JNI_VERSION_1_6) ! JNI_OK) return JNI_ERR; // 方法注册代码... return JNI_VERSION_1_6; }2.2 AndroidManifest.xml关键配置必须关注的manifest配置项uses-permission android:nameandroid.permission.INTERNET/ uses-feature android:glEsVersion0x00020000 android:requiredtrue/ application android:nameorg.qtproject.qt5.android.bindings.QtApplication android:extractNativeLibstrue activity android:nameorg.qtproject.qt5.android.bindings.QtActivity android:configChangesorientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation android:launchModesingleTop meta-data android:nameandroid.app.lib_name android:valueMyQtApp/ /activity /application2.3 处理Activity生命周期通过QtAndroid命名空间监听关键事件#include QtAndroid // 暂停/恢复回调 QAndroidJniObject::callStaticMethodvoid( org/qtproject/example/MyActivity, registerLifecycleCallbacks, (Landroid/app/Activity;)V, QtAndroid::androidActivity().object()); // 处理返回键 bool eventFilter(QObject *obj, QEvent *event) { if (event-type() QEvent::KeyPress) { QKeyEvent *keyEvent static_castQKeyEvent*(event); if (keyEvent-key() Qt::Key_Back) { // 自定义返回逻辑 return true; } } return QObject::eventFilter(obj, event); }3. 性能优化与疑难排解3.1 启动时间优化方案实测数据对比Redmi Note 10 Pro优化措施冷启动时间(ms)热启动时间(ms)默认配置28001200禁用QML调试2100900预加载资源1800700AOT编译1500500具体实现在main.cpp中提前加载资源Q_INIT_RESOURCE(core); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);使用Android的SplashScreen API// styles.xml style nameLaunchTheme parentTheme.SplashScreen item namewindowSplashScreenBackgroundcolor/launch_background/item item namewindowSplashScreenAnimatedIcondrawable/launch_icon/item item namepostSplashScreenThemestyle/AppTheme/item /style3.2 内存管理要点常见内存泄漏场景QML中的JavaScript对象未及时释放JNI局部引用未DeleteLocalRef跨线程QObject未moveToThread检测工具组合Android Studio ProfilerQt Creator的QML Profiler手动重载new/delete运算符记录分配void* operator new(size_t size) { void* p malloc(size); MemoryTracker::instance().add(p, size); return p; } void operator delete(void* p) { MemoryTracker::instance().remove(p); free(p); }3.3 图形渲染优化OpenGL ES最佳实践使用QQuickFramebufferObject替代Canvas限制QML中的ShaderEffect数量纹理压缩格式选择android { ANDROID_EXTRA_LIBS \ $$PWD/libs/arm64-v8a/libastcenc.a \ $$PWD/libs/armeabi-v7a/libastcenc.a }Vulkan后端启用方法Qt 6configure -vulkan -no-opengl4. 高级功能集成实战4.1 蓝牙模块深度集成完整蓝牙通信流程实现在AndroidManifest中添加权限uses-permission android:nameandroid.permission.BLUETOOTH/ uses-permission android:nameandroid.permission.BLUETOOTH_ADMIN/ uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION/Qt中通过JNI调用Android蓝牙APIQAndroidJniObject adapter QAndroidJniObject::callStaticObjectMethod( android/bluetooth/BluetoothAdapter, getDefaultAdapter, ()Landroid/bluetooth/BluetoothAdapter;); if (adapter.isValid()) { QAndroidJniObject name adapter.callObjectMethod( getName, ()Ljava/lang/String;); qDebug() Bluetooth name: name.toString(); }实现BluetoothGattCallback回调public class QtBluetoothGattCallback extends BluetoothGattCallback { private native void onCharacteristicChanged(long pointer, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic); Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { onCharacteristicChanged(nativePointer, gatt, characteristic); } }4.2 动态图标与主题切换实现Material You动态取色创建ColorScheme.ktComposable fun rememberDynamicColorScheme(): ColorScheme { val context LocalContext.current val dynamicColor Build.VERSION.SDK_INT Build.VERSION_CODES.S return if (dynamicColor) { val scheme dynamicDarkColorScheme(context) scheme.copy( primary Color(0xFF6750A4), secondary Color(0xFFE8DEF8) ) } else { darkColorScheme( primary Color(0xFFD0BCFF), secondary Color(0xFFCCC2DC) ) } }Qt中监听主题变化QAndroidJniObject activity QtAndroid::androidActivity(); QAndroidJniObject config activity.callObjectMethod( getResources, ()Landroid/content/res/Resources;).callObjectMethod( getConfiguration, ()Landroid/content/res/Configuration;); int uiMode config.getFieldint(uiMode) Configuration.UI_MODE_NIGHT_MASK; bool isDark (uiMode Configuration.UI_MODE_NIGHT_YES);4.3 跨进程窗口嵌入方案实现Qt窗口嵌入Chrome浏览器的关键技术点获取SurfaceTextureQAndroidJniObject surfaceTexture QAndroidJniObject(android/graphics/SurfaceTexture, (I)V, textureId); QAndroidJniObject surface QAndroidJniObject(android/view/Surface, (Landroid/graphics/SurfaceTexture;)V, surfaceTexture.object()); // 传递给宿主进程 QAndroidIntent intent; intent.handle().callObjectMethod( putExtra, (Ljava/lang/String;Landroid/os/Parcelable;)Landroid/content/Intent;, QAndroidJniObject::fromString(qt_surface).object(), surface.object());解决输入事件传递问题public boolean onTouchEvent(MotionEvent event) { // 坐标转换 int[] loc new int[2]; getLocationOnScreen(loc); event.offsetLocation(-loc[0], -loc[1]); // 转发到Qt进程 return nativeHandleTouch(nativePtr, event); }5. 部署与发布策略5.1 应用签名与加固推荐的多渠道打包方案使用Android App BundleAAB格式在gradle中配置flavorandroid { flavorDimensions abi productFlavors { armv7 { dimension abi ndk { abiFilters armeabi-v7a } } arm64 { dimension abi ndk { abiFilters arm64-v8a } } } }Qt编译时自动注入版本信息VERSION 1.2.3 android { DEPLOYMENT_DEPENDENCIES android/AndroidManifest.xml ANDROID_VERSION_CODE $$shell_printf(%d%02d%02d00, \ $$section(VERSION, ., 0, 0), \ $$section(VERSION, ., 1, 1), \ $$section(VERSION, ., 2, 2)) }5.2 动态功能模块化按需加载Qt模块的实现创建Dynamic Feature Module修改base模块的build.gradleandroid { dynamicFeatures [:qt_features] }使用Play Core Library请求安装SplitInstallManager splitInstallManager SplitInstallManagerFactory.create(context); SplitInstallRequest request SplitInstallRequest.newBuilder() .addModule(qt_features) .build(); splitInstallManager.startInstall(request) .addOnSuccessListener(sessionId - { /* 加载成功 */ }) .addOnFailureListener(exception - { /* 处理错误 */ });5.3 崩溃分析与监控Qt-Android崩溃捕获方案实现Java层的UncaughtExceptionHandlerpublic class QtCrashHandler implements Thread.UncaughtExceptionHandler { private static native void handleNativeCrash(String stackTrace); Override public void uncaughtException(Thread t, Throwable e) { StringWriter sw new StringWriter(); e.printStackTrace(new PrintWriter(sw)); handleNativeCrash(sw.toString()); } }C层信号捕获#include csignal void signalHandler(int signum) { qCritical() Signal signum received; printStackTrace(); std::exit(1); } void installHandlers() { signal(SIGSEGV, signalHandler); signal(SIGABRT, signalHandler); qInstallMessageHandler(qtMessageHandler); }集成Firebase CrashlyticsFirebaseCrashlytics.getInstance().setCustomKey(qt_version, QtBuildVersion.QT_VERSION_STR); FirebaseCrashlytics.getInstance().recordException(e);
返回列表