I am trying to integrate the HockeyApp Android SDK in its latest version 4.1.2 into our Qt Android app. I don't know a lot about Android development or Java in general - nor any Qt or HockeyApp internals.
There is some general official documentation on including third party Android (i.e. Java) libraries into a Qt project; however, it mentions a "Create AndroidManifest.xml button in the Deployment settings" which does not exist in the current Qt Creator 4.2.0 version, so I am unsure as to how outdated this documentation is or what parts possibly still apply.
HockeyApp documentation only covers "typical Android development" using Android Studio and their support is not supportive.
On a side note, integrating the HockeyApp iOS SDK took just a couple of hours and was mostly a matter of writing some Objective C bridge code.
Step 1: Add the SDK to the project
First approach
I tried to follow the above documentation and added the unzipped HockeySDK-Android-4.1.2
folder and a project.properties
file with contents
android.library.reference.1=HockeySDK-Android-4.1.2/libs/
to a project folder; I also set ANDROID_PACKAGE_SOURCE_DIR in <my project>.pro
to that project folder.
Problem: The androiddeployqt
build step issues an error message (wrapped for readability)
Error: <path to build folder>/android-build/HockeySDK-Android-4.1.2/libs
is not a valid project (AndroidManifest.xml not found).
but continues anyway and finally fails with an error message (also wrapped for readability)
BUILD FAILED
/opt/Android/android-sdk-macosx/tools/ant/build.xml:573:
HockeySDK-Android-4.1.2/libs/ resolve to a path
with no project.properties file for project <path to build folder>/android-build
Running androiddeployqt
with a --verbose
switch adds significantly more output, but zero useful information.
Second approach
The downloaded HockeyApp Android SDK zip archive contains - besides some documentation - a file libs/HockeySDK-4.1.2.aar
; from this response and this comment, I gather the AAR format is simply a zip archive that, among others, contains an AndroidManifest.xml
file. I unzipped libs/HockeySDK-4.1.2.aar
in place, then removed it; now the first error message in the build is gone.
Problem: The build fails with
BUILD FAILED
/opt/Android/android-sdk-macosx/tools/ant/build.xml:597:
The following error occurred while executing this line:
/opt/Android/android-sdk-macosx/tools/ant/build.xml:649:
The following error occurred while executing this line:
/opt/Android/android-sdk-macosx/tools/ant/build.xml:655:
<path to build folder>/android-build/HockeySDK-Android-4.1.2/libs/src does not exist.
Again, adding running --verbose
to androiddeployqt
adds nothing but noise. Looking into the build.xml
locations mentioned doesn't help either.
Update:
I tried simply creating the missing folder from the error message; now the build fails as follows:
-dex:
[dex] input: <path to build folder>/android-build/bin/classes
[dex] input: <path to build folder>/android-build/HockeySDK-Android-4.1.2/libs/bin/classes.jar
[dex] input: <path to build folder>/android-build/libs/QtAndroid-bundled.jar
[dex] input: <path to build folder>/android-build/libs/QtAndroidBearer-bundled.jar
[dex] Pre-Dexing <path to build folder>/android-build/libs/QtAndroid-bundled.jar -> QtAndroid-bundled-a06280f40655c27b25038380a4d7f67c.jar
[dex] Pre-Dexing <path to build folder>/android-build/libs/QtAndroidBearer-bundled.jar -> QtAndroidBearer-bundled-a69fa323dbfa477411ea082423128813.jar
[dex] Converting compiled files and external libraries into <path to build folder>/android-build/bin/classes.dex...
[dx]
[dx] UNEXPECTED TOP-LEVEL EXCEPTION:
[dx] java.io.FileNotFoundException: <path to build folder>/android-build/HockeySDK-Android-4.1.2/libs/bin/classes.jar (No such file or directory)
[dx] at java.util.zip.ZipFile.open(Native Method)
[dx] at java.util.zip.ZipFile.<init>(ZipFile.java:219)
[dx] at java.util.zip.ZipFile.<init>(ZipFile.java:149)
[dx] at java.util.zip.ZipFile.<init>(ZipFile.java:163)
[dx] at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:244)
[dx] at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)
[dx] at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
[dx] at com.android.dx.command.dexer.Main.processOne(Main.java:677)
[dx] at com.android.dx.command.dexer.Main.processAllFiles(Main.java:574)
[dx] at com.android.dx.command.dexer.Main.runMonoDex(Main.java:311)
[dx] at com.android.dx.command.dexer.Main.run(Main.java:277)
[dx] at com.android.dx.command.dexer.Main.main(Main.java:245)
[dx] at com.android.dx.command.Main.main(Main.java:106)
[dx] 1 error; aborting
BUILD FAILED
/opt/Android/android-sdk-macosx/tools/ant/build.xml:888: The following error occurred while executing this line:
/opt/Android/android-sdk-macosx/tools/ant/build.xml:890: The following error occurred while executing this line:
/opt/Android/android-sdk-macosx/tools/ant/build.xml:902: The following error occurred while executing this line:
/opt/Android/android-sdk-macosx/tools/ant/build.xml:283: null returned: 1
I obviously can not just make up some classes.jar
file, but there is one single file with that name in <path to build folder>/android-build/HockeySDK-Android-4.1.2/libs
, so I can create the missing bin
folder and create a symlink in it to ../classes.jar
.
The build now succeeds. Step 1 complete.
Step 2: Use the SDK in the project
Going back to the corresponding HockeyApp documentation, I gather I need to modify some code that looks similar to
public class YourActivity extends Activity
...
There is one single occurrence of such code in the project in <path to build folder>/android-build/src/org/qtproject/qt5/android/bindings/QtActivity.java
:
public class QtActivity extends Activity
...
That file is copied there by the androiddeployqt
tool (part of the Qt SDK) from its source folder inside the Qt SDK at $QTDIR/src/android/java
, i.e. /opt/Qt/Qt_5.7.1/5.7/android_armv7/src/android
on my machine.
Looking at the androiddeployqt
source code, I see there is no way (e.g. by means of a command line parameter) to change the source folder these .java
files are taken from, so it is not possible to provide my own set of files and have androiddeployqt
copy that instead. Hence, in order to make this work, I must either extend / fix androiddeployqt
functionality (sorry, I won't touch that code - yuck!) or modify the copied files at their source directly - which then obviously affects all projects build with that Qt SDK instance.
As a third approach, I could try patching the Java sources after androiddeployqt
copied them to the build folder. Unfortunately, this not only makes the development and build workflow absurdly painful; it is also made entirely impossible by the androiddeployqt
"one tool does everything" design fail (see below): There is no point in time in the build process where the Java sources exist in the project and the .apk
package has not been built yet. I thought the androiddeployqt
--no-build
parameter would enable that:
--no-build: Do not build the package, it is useful to just install
a package previously built.
but "building" here refers to building the Qt / Java bridge code, not the actual app, so that parameter turns out to be pretty much useless for the task at hand; the build fails with this message - which by itself are a bug as the .apk
file from the message does not exist and thus the Android package build was not successful:
Android package built successfully in 1.011 ms.
-- File: <path to build folder>/android-build//bin/QtApp-release-unsigned.apk
Sure, I could copy the sources elsewhere, patch and copy them manually to their final destination, but I think this is where I draw the line.
Actually, looking at the androiddeployqt
source code is where the buck stops. Too much time has already been wasted trying to get dysfunctional tools to work:
- I will not comment on how smart it is to write a build tool in C++ in the first place - in a single, ~3000 line
main.cpp
file
- I will not comment on the practice of hardcoding the
src/android/java
path (and many other paths) in the androiddeployqt
sources not once, but multiple times - instead of a const QString
that at least prepares appropriate (i.e. external) tool configuration
- I will not comment on how smart it is to violate the ~50 year old best practice of one tool, one purpose - and duplicate large amounts of functionality provided by Qt, Android or Java tools, such as building, signing, deploying, installing packages, etc.
- However, the fact that this tool has made it past code review into a number of major Qt release is nothing short of frightening - and probably a good indicator that it's time to consider dropping Qt as a technology in general
While I am writing this, the Qt 5.8.0 installation keeps freezing, maxing out one CPU core - and has been doing so for the last ~12 hours. I rest my case.
My questions
has anyone had any success with Qt, Android and HockeyApp ?
does anyone have any hints / pointers / wild guesses as to what is wrong here ?
- no further questions; I've seen enough
Step 3: Call SDK Java code from C++
(cancelled due to brickwall in step 2)