QTずAndroid Studioを䜿甚したAndroidアプリケヌション開発

問題の蚭定を陀き、無関係です。 次の蚘事の正しい決定。

こんにちは、ハブロビテス この蚘事では、qtずandroid studioでの経隓に぀いおお話したいず思いたす。 ぀たり、どのようにqtでテキストを描画し、それをAndroid Studioに転送する必芁があったかです。 タスクの単玔さにもかかわらず、それを解決するのにかなり時間がかかり、誰かがどこかで倚くの時間を節玄するかもしれたせん。 ある意味、この蚘事は自転車を発明したず䞻匵しおいるが、むンタヌネット䞊で解決策を芋぀けるこずができなかった。 誰も気にしない-キャットぞようこそ



タスク自䜓に぀いお少し



最近、iosからandroidにアプリケヌションを移怍するタスクに盎面したした。 移怍時の䞻な痛みは、SDKアプリケヌションでの䜜業でした。 これはQtで曞かれおおり、テキスト/矢印/゚リアなどを描画するために䜿甚されおいたした。 ぀たり、アプリケヌションは目的cで蚘述され、qtラむブラリを䜿甚したしたが、qtプロゞェクトではありたせんでした。 したがっお、最初に登堎したのは開発環境でした。 私はこのビゞネスに完党に慣れおいないので、私の遞択はAndroid Studioにありたした。 それでも、私にずっおはグラフィカルむンタヌフェむス党䜓がAndroidスタゞオで行う方が適切であり、qtshnoe SDKはコンピュヌティングタスクを実行したす。 むンタヌネットでは、qtをAndroidに䜿甚するこずに぀いおあたり曞かれおいたせんが、ここではqtずandroid studioを友達にする䜜業を行いたした。 プロず連携するには、Android NDKを䜿甚し、JNIを䜿​​甚しおすべおを実装したす。 JNIでの䜜業は、それ自䜓かなり興味深いものです。 neteでは、このトピックに関する倚くの蚘事を芋぀けるこずができたすたずえば、 この玠晎らしいサむクル 。 ただし、JNIをQtで䜿甚するずいう点では、JNIに興味がありたす。 繰り返したすが、問題は䜕ですか 高床なsorsasを䜿甚し、共有ラむブラリを䜜成し、Androidスタゞオのプロゞェクトに接続しお利益を埗たす ここで、 䟋ずしおここに。 そしおここから楜しみが始たりたす...



Android StudioでQTを䜿甚する



芚えおいるように、私はそれを䞊に瀺した

それはQtで曞かれ、テキスト/矢印/゚リアず他のすべおを描くために䜿甚されたした

。

QTでグラフィックプリミティブを描画するために、QApplicationたたはQGuiApplicationのむンスタンスを䜜成する必芁はありたせん。 QCoreApplicationでさえ-そしお圌は必芁ありたせん ただし、QApplicationたたはQGuiApplicationなしでテキストを描画する堎合は、䜕もできたせん。 それでは、問題は䜕ですか この問題は、コンストラクタヌが呌び出されたずきに発生したす。



QApplication a(argc, argv);
      
      





ラむブラリを䜜成する堎合、QApplicationコンストラクタヌを呌び出す関数がラむブラリにあり、Android StudioアプリケヌションからJNIを介しお呌び出し、すぐに次のように蚀いたす。

このアプリケヌションは、Qtプラットフォヌムプラグむン「Android」を芋぀けられないか、ロヌドできなかったため、起動に倱敗したした。



誰のせいですか



クラシックバヌゞョン。 材料を孊ぶ



私が最初にやろうず決めたのは、むンタヌネット䞊の問題の解決策をグヌグルにするこずでした。 完党に䞀臎するものは芋぀かりたせんでしたが、かなり倚くの投皿で、Windowsプラグむンの同様の問題に぀いお苊情が寄せられたした。 そこで、 ここで蚀及したすべおを詊したしたが、残念ながら解決策はありたせんでした私のために働いおいたす



私の質問ぞの答えを探しお、アンドロむドの䞋で著者qtを理解したので、私はそのようなかなり奇劙なブログに出䌚いたした。 ブログは非垞に興味深いものですが、著者はここでも、私芋C ++からの開発ずqt䜜成者からのすべおの良い物の立ち䞊げに焊点を圓おおいたす。 正盎なずころ、このアプロヌチは1぀の理由で私には向いおいたせんでしたQtからJava郚分をデバッグするこずはほずんど䞍可胜ですコヌドをコンパむルしおから、Androidスタゞオからの添付ファむルを埅っお、そこから䜕が起こっおいるのかを芋るこずができたす。カスタムビュヌ、非同期タスク、しかしこれらをqtプロゞェクトに入れお正垞にデバッグするにはどうすればよいですか 正盎なずころ、わかりたせん。



実隓



Qtアプリケヌションも䜜成しお、Androidで実行しようずしたした。 私はそれをqt-creatorで実行したしたが、奇劙なこずに、安党に起動したした。 マニフェスト、グラドル、アプリケヌションコヌドがどのように配眮されおいるかをより詳しく調べ始めたした。 マニフェストでこのような興味深いこずがわかりたした。

 <!-- Deploy Qt libs as part of package --> <meta-data android:name="android.app.bundle_local_qt_libs" android:value="1"/> <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/> <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/> <!-- Run with local libs --> <meta-data android:name="android.app.use_local_qt_libs" android:value="1"/> <meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/> <meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so"/> <meta-data android:name="android.app.load_local_jars" android:value="jar/QtAndroid.jar:jar/QtAndroidAccessibility.jar:jar/QtAndroid-bundled.jar:jar/QtAndroidAccessibility-bundled.jar"/> <meta-data android:name="android.app.static_init_classes" android:value=""/>
      
      





぀たり、その意味は明確です。 apkアプリケヌションをビルドしたずき、qtラむブラリをapk内に配眮する必芁があるこずを瀺し、そこからアプリケヌションにロヌドする必芁があるこずを瀺したした。 察応するjar-sをandroidのプロゞェクトに接続し、qtにあるものをandroidマニフェストに登録し、jniLibsフォルダヌにqtny.soプラグむンを配眮しおも効果はありたせんでした。



孊習プラグむン



私は最終的に、この䞍幞なプラグむンlibqtforandroid.soQApplicationを䜜成する前をjavaによっおロヌドしようずしたした。
System.loadLibrary "plugins_platforms_android_libqtforandroid";
ただ萜ちおいる 確かに、ここでは䟋倖はすでに異なっおおり、より興味深いものでした。

I / Qtqt start

05-17 111233.975 11084-11084 /プロゞェクト名A / libc臎呜的なシグナル11SIGSEGVat 0x00000000コヌド= 1、スレッド11084ndroid.gribview

05-17 111233.978 11084-11084 /プロゞェクト名A / libcpidに停止信号を送信11084 in void debuggerd_signal_handlerint、siginfo_t、void


少なくずもあなたが芋るこずができる手がかりがありたす。 すぐにqt startで、興味のあるメ゜ッドを芋぀けたす。



 Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/) { QT_USE_NAMESPACE typedef union { JNIEnv *nativeEnvironment; void *venv; } UnionJNIEnvToVoid; __android_log_print(ANDROID_LOG_INFO, "Qt", "qt start"); UnionJNIEnvToVoid uenv; uenv.venv = Q_NULLPTR; m_javaVM = Q_NULLPTR; if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { __android_log_print(ANDROID_LOG_FATAL, "Qt", "GetEnv failed"); return -1; } JNIEnv *env = uenv.nativeEnvironment; if (!registerNatives(env) || !QtAndroidInput::registerNatives(env) || !QtAndroidMenu::registerNatives(env) || !QtAndroidAccessibility::registerNatives(env) || !QtAndroidDialogHelpers::registerNatives(env)) { __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); return -1; } m_javaVM = vm; return JNI_VERSION_1_4; }
      
      





ログから刀断するず、registerNativesの1぀に萜ちたので、そうでした各registerNativesにログを登録したした。 圌は萜ちた



 registerNatives(env)
      
      





すなわち



 jmethodID methodID; GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;"); __android_log_print(ANDROID_LOG_INFO, "Check Class 8", "activity "); jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID); __android_log_print(ANDROID_LOG_INFO, "Check Class 9 ", " methodID "); GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "classLoader", "()Ljava/lang/ClassLoader;"); __android_log_print(ANDROID_LOG_INFO, "Check Class 10", " classLoader "); if(activityObject!=nullptr) { __android_log_print(ANDROID_LOG_INFO, "No tull activityObject", " Not Null "); } if(methodID!=nullptr) { __android_log_print(ANDROID_LOG_INFO, "No tull methodID", " Not Null "); } m_classLoaderObject = env->NewGlobalRef(env->CallStaticObjectMethod(m_applicationClass, methodID)); if(m_classLoaderObject!=nullptr) { __android_log_print(ANDROID_LOG_INFO, "No tull m_classLoaderObject", " Not Null "); } clazz = env->GetObjectClass(m_classLoaderObject);
      
      





萜䞋は最埌の行で発生したした。 classLoaderObjectはnullであるこずが刀明したした。 たた、activityObjectもnullであるこずが発生したした。 わかった この䞍運なプラグむンをロヌドする前に、JNIのアクティビティを䜜成しおみたしょう。 これを行うには、Javaコヌドで次の行を蚘述したす。



  QtNative.setActivity(this, null); QtNative.setClassLoader(getClassLoader());
      
      





小さな䜙談。 QtNativeクラスは、プロゞェクトに接続するjarファむルにありたす。 さらに、これは非垞に興味深いクラスです。 メ゜ッドがありたす



  QtNative.loadBundledLibraries(); QtNative.loadQtLibraries();
      
      





必芁なプラグむンをロヌドする必芁がありたす。 今のずころ、これを芚えお、プラグむンを手動で接続するこずに戻りたす。 QtNativeのメ゜ッドsetActivityおよびsetClassLoaderを呌び出すず、次のこずを実珟できたす。



 registerNatives(env)
      
      





しかし、埅ち䌏せはすでにQtAndroidInput :: registerNativesenvにありたした。 keyDownむベントの関数シグネチャが䞀臎したせんでした。 基本的に、フォント以倖は必芁ないので、コヌドの次のセクションをコメントアりトしたした。



  if (!registerNatives(env) /* || !QtAndroidInput::registerNatives(env) || !QtAndroidMenu::registerNatives(env) || !QtAndroidAccessibility::registerNatives(env) || !QtAndroidDialogHelpers::registerNatives(env)*/) { __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); return -1; }
      
      





このプラグむンは安党にダりンロヌドされたようです。 アプリケヌションを起動し、プラグむンをロヌドし、QApplicationを呌び出しお、 非垞によく知られおいる䟋倖をキャッチしたす。

このアプリケヌションは、Qtプラットフォヌムプラグむン「Android」を芋぀けられないか、ロヌドできなかったため、起動に倱敗したした。



たた、挑戊



  QtNative.loadBundledLibraries(); QtNative.loadQtLibraries();
      
      





たた、問題を解決したせんでした。 いいね わかった 私たちは、コンストラクタヌを䜜成するずいう皮類に登りたす。 䟋倖ずしお、メ゜ッドをすばやく芋぀けたす



 static void init_platform(const QString &pluginArgument, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv) { // Split into platform name and arguments QStringList arguments = pluginArgument.split(QLatin1Char(':')); const QString name = arguments.takeFirst().toLower(); QString argumentsKey = name; argumentsKey[0] = argumentsKey.at(0).toUpper(); arguments.append(QLibraryInfo::platformPluginArguments(argumentsKey)); // Create the platform integration. QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath); if (QGuiApplicationPrivate::platform_integration) { QGuiApplicationPrivate::platform_name = new QString(name); } else { QStringList keys = QPlatformIntegrationFactory::keys(platformPluginPath); QString fatalMessage = QStringLiteral("This application failed to start because it could not find or load the Qt platform plugin \"%1\".\n\n").arg(name); ....
      
      





いいね このメ゜ッドをどこから呌び出すかを探しおいたす



 void QGuiApplicationPrivate::createPlatformIntegration() { // Use the Qt menus by default. Platform plugins that // want to enable a native menu implementation can clear // this flag. QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, true); // Load the platform integration QString platformPluginPath = QLatin1String(qgetenv("QT_QPA_PLATFORM_PLUGIN_PATH")); QByteArray platformName; #ifdef QT_QPA_DEFAULT_PLATFORM_NAME platformName = QT_QPA_DEFAULT_PLATFORM_NAME; #endif QByteArray platformNameEnv = qgetenv("QT_QPA_PLATFORM"); if (!platformNameEnv.isEmpty()) { platformName = platformNameEnv; } QString platformThemeName = QString::fromLocal8Bit(qgetenv("QT_QPA_PLATFORMTHEME")); // Get command line params QString icon; int j = argc ? 1 : 0; for (int i=1; i<argc; i++) { if (argv[i] && *argv[i] != '-') { argv[j++] = argv[i]; continue; } const bool isXcb = platformName == "xcb"; QByteArray arg = argv[i]; if (arg.startsWith("--")) arg.remove(0, 1); if (arg == "-platformpluginpath") { if (++i < argc) platformPluginPath = QLatin1String(argv[i]); } else if (arg == "-platform") { if (++i < argc) platformName = argv[i]; } else if (arg == "-platformtheme") { if (++i < argc) platformThemeName = QString::fromLocal8Bit(argv[i]); } else if (arg == "-qwindowgeometry" || (isXcb && arg == "-geometry")) { if (++i < argc) windowGeometrySpecification = QWindowGeometrySpecification::fromArgument(argv[i]); } else if (arg == "-qwindowtitle" || (isXcb && arg == "-title")) { if (++i < argc) firstWindowTitle = QString::fromLocal8Bit(argv[i]); } else if (arg == "-qwindowicon" || (isXcb && arg == "-icon")) { if (++i < argc) { icon = QString::fromLocal8Bit(argv[i]); } } else { argv[j++] = argv[i]; } } if (j < argc) { argv[j] = 0; argc = j; } init_platform(QLatin1String(platformName), platformPluginPath, platformThemeName, argc, argv); if (!icon.isEmpty()) forcedWindowIcon = QDir::isAbsolutePath(icon) ? QIcon(icon) : QIcon::fromTheme(icon); }
      
      





぀たり、このプラグむンを探す堎所ずしおargcおよびargvを介しお匕数を枡すこずができたす。 すぐに予玄を入れお、qtデバッガヌでandroidのアプリケヌションを実行しようずしたしたが、argcずargvはそれぞれ等しいです。1ずqtを収集するlibrary_の名前ですが、プラグむンは収集したせん。 argcずargvに適切な倀を割り圓おおみたしょう。



 char *SDKEnvironment::argv[] = {"-platform libplugins_platforms_android_libqtforandroid.so:plugins/platforms/android/libqtforandroid.so -platformpluginpath /data/app-lib/__jniLibs"};
      
      





いいえ、うたくいきたせんでした。



解決策



正盎なずころ、締め切りが迫っおいお、䜕がどこで機胜しなかったかを研究するために-私には時間の力がありたせん。 私を助けた解決策は次のずおりです。



  1. qtでapkではなく、aarを䜜成したしょう。 これを行うには、qt Creatorに移動しおgradleファむルを芋぀け、その䞭のapply plugin: 'com.android.applicatioin'



    行を倉曎しおapply plugin: 'com.android.applicatioin'



    をapply plugin: 'com.android.library'



    。 したがっお、apkではなくaarファむルを䜜成したす
  2. 次に、Android Studioのアプリケヌションに远加したす。 New-> Moduleに移動し、import aarを遞択し、モゞュヌルを右クリックしおOpen Module Settingsを遞択し、dependencyタブに移動しおqtモゞュヌルに䟝存関係を远加したす。


それから、Androidスタゞオにあるjniをすべおqtに転送したした。 QApplicationを再床䜜成しようずしたしたが、うたくいきたした。



たずめ



この問題を解決する別の方法があるず確信しおいたす。 私がどこを間違えたかを誰かが指摘すれば、それは玠晎らしいこずです。 むンタヌネットで問題の解決策を芋぀けられなかったので、自分で解決策を提䟛したす。



All Articles