ImaginativeThinking.ca


A developers blog

What the heck how do I share Java code between Qt Android projects?

By: Brad

Hey Brad in Qt for Android you can have Java files included in your project for accessing native API’s. When building you need to put these Java files in a special src directory to be property picked up by the Android SDK; you tell qMake where to find these files (and other Android required files like resources and the manifest file) by using the ANDROID_PACKAGE_SOURCE_DIR variable in your project *.pro file. What if some of the Java files I want to include in my App are common and I want to share them between multiple projects? How the heck do I tell qMake to copy them all to the src folder when I build the App?

QtAndroidPig

I’m still fairly new to developing Android apps in Qt but recently I started the journey and ran into the exact same problem your having; I wrote some common C++ / Java code that I wanted to share between a few different apps. The C++ code was easy (shared library) however as you say what about the Java code, I don’t want to have copies of the same Java code in each project directory.

First I thought I could use the += operator and provide two paths to the ANDROID_PACKAGE_SOURCE_DIR but that just resulted in qMake thinking the path was “C:\myProject\android C:\MyCommon\android” which obviously isn’t a valid path.

It took a bit but I figured out a way to do it that is pretty easy to do.

When compiling a Qt Android project after the C++ code is compiled/linked an ANT script is run which compiles the Java code (hence why you just need to stick Java source files and not compiled JAR files in the src folder) and creates the Android Application Package (APK).

The ANT script, which is part of the Android SDK, by default looks in the src folder and compiles any Java files found there. So all you really need to make sure is that before the ANT script is run all the Java files you want to use as part of your App are located in that folder.

The ANDROID_PACKAGE_SOURCE_DIR variable tells qMake where to find the files it needs to copy to the android-build folder; which is where all the compiled C++ and other files (i.e. Java source code files, manifest file, assets, etc.) need to go for the ANT script to work on in order to generate an APK. You can optionally put a src folder in the directory you set to ANDROID_PACKAGE_SOURCE_DIR and put any project specific Java source code files in it to make sure they can copied to the android-build/src folder and compiled into your APK. What we want however is a way to also copy common Java source code files, which we store in a common location on disk separately from the project specific Java source code files, to the android-build/src folder. To do this we can create a custom Make target which is run some time before the ANT script is executed to manually copy our common Java files to the android-build/src folder so they can be compiled into the App too since the ANT script will compile any Java source code files it finds in said folder.

To do this in my applications *.pro file I added the following:

# This line makes sure my custom manifest file and project specific java code is copied to the android-build folder
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android

# This is a custom variable which holds the path to my common Java code
# I use the $$system_path() qMake function to make sure that my directory separators are correct for the platform I'm compiling on as you need to use the correct separator in the Make file (i.e. \ for Windows and / for Linux)
commonAndroidFilesPath = $$system_path( $$PWD/../CommonLib/android-sources/src )

# This is a custom variable which holds the path to the src folder in the output directory. That is where they need to go for the ANT script to compile them.
androidBuildOutputDir = $$system_path( $$OUT_PWD/../android-build/src )

# Here is the magic, this is the actual copy command I want to run.
# Make has a platform agnostic copy command macro you can use which substitutes the correct copy command for the platform you are on: $(COPY_DIR)
copyCommonJavaFiles.commands = $(COPY_DIR) $${commonAndroidFilesPath} $${androidBuildOutputDir}

# I tack it on to the 'first' target which exists by default just because I know this will happen before the ANT script gets run.
first.depends = $(first) copyCommonJavaFiles
export(first.depends)
export(copyCommonJavaFiles.commands)
QMAKE_EXTRA_TARGETS += first copyCommonJavaFiles

When you run qMake the Make file that gets generated will have the following:

first: $(first) copyCommonJavaFiles

copyCommonJavaFiles:
    $(COPY_DIR) C:\Users\bvanderlaan\Documents\GitHub\MyProject\MyProjectApp\..\CommonLib\android-sources\src C:\Users\bvanderlaan\Documents\GitHub\build-MyProject-Android_for_armeabi_v7a_GCC_4_9_Qt_5_4_1-Debug\MyProjectApp\..\android-build\src

So now when I build the application my common C++ gets linked in as a shared library and my common Java code gets copied to the android-build/src directory before the ANT script compiles all the Java code that it finds, both project specific and common, and packages them into my apps APK. This eliminates any need to keep copies of the Java files in multiple locations in my source tree or need for external build scripts/tools to setup my working space before I compile.

So there you have it, a few extra qMake statements and you can store your common Java source files in a central location in your repository and have any apps which need to use them automatically copy them into their output directory each time you build.

Until next time think imaginatively and design creatively

Brad

My interest in computer programming started back in high school and Software Development has remained a hobby of mine ever since. I graduated as a Computer Engineering Technologist and have been working as a Software Developer for many years. I believe that software is crafted; understanding that how it is done is as important as getting it done. I enjoy the aesthetics in crafting elegant solutions to complex problems and revel in the knowledge that my code is maintainable and thus, will have longevity. I hold the designation Certified Technician (C.Tech.) with the Ontario Association of Computer Engineering Technicians and Technologists (OACETT), have been certified as a Professional Scrum Master level 1 (PSM I) and as a Professional Scrum Developer level 1 (PSD I) by Scrum.org as well as designated as an Officially Certified Qt Developer by the Qt Company. For more on my story check out the about page here

Feel free to write a reply or comment.