Brian Long Consultancy & Training Services
Ltd.
April 2014
There is an update to this article for Delphi XE6 available here.
Accompanying source files available through this download
link.
Delphi XE4 and Delphi XE5 both support building iOS applications. One of the neat things about a Delphi iOS app is that it automatically gets a splash screen by default. You simply customise which image is to be used and away you go.
This all works straightforwardly because iOS is set up ready to pop up the splash screen as specified in the application's configuration data during the loading/invocation process.
Unfortunately Android has no equivalent in-built splash screen behaviour, and as a consequence Delphi XE5 Android apps do not get a similar splash screen created for them. This is a little disappointing as, given the average large size of a Delphi XE5 Android app, its load time (certainly the first load time of the first invocation, at least) is often rather longer than we might wish on our users.
The Android package (.apk file) is an archive containing the compiled Delphi code in an Android native library, as well a bunch of compiled Java startup and support code in a classes.dex file and an optimised/crunched version of the various necessary Android resources and the application manifest. On first invocation the (sizeable) native library is pulled out of the archive by the Android package loader and stored outside for future immediate availability.
This operation can be quite tardy on devices that don't have cutting edge hardware and it's only when the library has been extracted that execution of your Delphi code can actually start. When an Android app starts, the FMX startup kicks off and does various setup jobs, such as copying any suitably located asset files from the package out onto the device storage and running the initialization sections of all units compiled into the library.
So the question arises as to how we can rectify this Delphi omission and set up a splash screen for an Android app built with XE5, the first version of Delphi that supports building Android apps.
It turns out there are several choices available to us. They vary in difficulty (to implement) and effectiveness, so let's briefly run through the available options.
Back in January 2014, Marco Cantù blogged about how to set up a splash screen for Android apps and this was re-posted on FMX Express here, by simply using a Delphi form in your Android app to do the job. If this form is the main form then the first thing the user sees is the splash screen, just as with a Delphi Win32 application.
The main argument against this approach is that the splash screen still won't appear until the native library has been extracted from the app package (on first invocation), the compiled Java startup startup code has been loaded and requested to call into the native Delphi library, the previously extracted Delphi library has then been loaded for execution purposes and all the FMX startup has run. On any moderate hardware the user is still likely to have a few seconds looking at a black screen waiting for the Delphi native code to start running, and quite possibly rather more than a few.
This is certainly the most straightforward approach to the problem but doesn't really help with the need for a splash screen caused by the very nature of Delphi XE5 Android apps - the time it takes to start executing Delphi code.
A splendid OS native solution to the problem is to modify your application's personal Android app theme. This involves adding a styles file in the Android resource directory tree, which defines an Android app theme, along with a referenced splash image. The Android manifest is then tweaked to reference the theme defined in this styles file.
This is quite straightforward, although it involves several steps and a few things set up in the Deployment Manager.
You can see the details of this approach in this Chinese blog post from November 2013 (if you read it in Chrome it will offer to translate it for you). Another run-through appears on this Korean blog post from February 2014, which has an accompanying sample application available. These are both referenced in this FMX Express post.
One down side to this approach is that after the splash screen has been displayed, if the app is running on hardware unsuitable for Delphi applications (i.e. an ARM v6 processor, an ARM v7 processor that has no NEON instruction support, or an Intel processor) then the app can still just gracelessly terminate with a system error message.
Note that as of Delphi XE7 and RAD Studio XE7 this is the approach used to implement the default splash screen support.
The third option was actually the first one to be publicised as an option for Delphi XE5 Android apps and I showed the technique in one of my two CodeRage 8 sessions back in October 2013, with the code made available in this blog post and reposted on FMX Express here.
This is the most complicated approach to the problem but has the advantage that it implements a splash screen in much the same way as Java Android programmers commonly implement splash screens. As soon as Android has loaded the application package and started running the main activity's code then the splash screen will appear, as the main activity either is the splash screen or directly invokes the splash screen.
The splash screen is actually written as Java code to accomplish this, and it is designed with a standard Android activity layout. One opportunity afforded by this approach is to run some sanity checking code to ensure the CPU hardware and Android OS version are actually compatible with Delphi applications.
This third option was the approach I took when considering how to solve this problem and so the rest of this article looks at how we can accomplish the goal.
Delphi apps on Android are native code libraries starting at a few megabytes in size in size, packaged into an .apk file along with some compiled Java, a few resources and an Android manifest. The native ARM code can communicate with the Java world using the Java Bridge (or JNI Bridge as it is sometimes called), but this relies on Delphi representations of the Java classes being present.
This is quite like Delphi for Win32 talking to Windows APIs - many APIs are pre-declared for you and some you need to create the declarations for yourself, if they aren't catered for by the Delphi RTL. Similarly Delphi XE5 has many Android classes represented, but many more are not, so there is good scope for being required to get on top of Delphi's Java Bridge to call into the Android API.
FireMonkey provides much functionality necessary for business applications needing access to data and display it to the user, but some common aspects of the Android OS are not 'wrapped up' in FireMonkey classes. In cases where this isn't simply a case of calling into more APIs whose declarations need to declared with Java Bridge interfaces this poses a problem.
Some examples of areas of the Android OS that are not wrapped up in XE5 and are difficult to incorporate are:
In Delphi XE5 these things and more require additional Java code to implement. Then some light hacking is needed to make use of this Java code in a Delphi package, which tends to get in the IDE's way, so IDE building/installing of the application becomes interesting, and debugging becomes next to impossible. These consequences tend to force you into managing a pair of synchronised projects:
The specifics of the approach will require some of the aforementioned light hacking. This may seem rather onerous and cumbersome for such a simple result as creating a splash screen, but becoming familiar with circumventing the normal Delphi build cycle becomes useful when you decide you need some of the other Android aspects that also require some of the same jiggery-pokery.
One of the benefits of this splash screen implementation approach is that it does take the opportunity to do some hardware checking and fail gracefully if the hardware or OS is not suitable for running a Delphi XE5 app. In Delphi XE6 this particular benefit goes away as the FMX Java startup code does this for us.
The steps required for the splash screen setup are as follows:
dx.bat
DexMerger
classThat's quite a daunting list but fear not. Fortunately many of the Java-oriented steps can be wrapped up by a suitably crafted batch (.bat) file or command script (.cmd) file. And I'm sure if you really wanted to, you could also create a PowerShell script to do the same, but I've stuck with the old scripting that I know how to work with.
Android resource files usually reside in a res
folder under the project
directory, so we'll stick with that norm.
There are three resource files required to get the splash screen running as required here:
The layout resource is a file called res\layout\splash-activity.xml
and contains this layout:
This defines an Android layout that fills its parent (essentially the screen) and
has an image view control centred inside it containing a drawable resource (an image)
called splash
. The image will be made as large as the image.
To set up the image we can either create a suitable file as res\drawable\splash.png
or, as is the case in this example, we can have the Deployment
Manager do that later.
Finally, the string values file is called res\values\strings.xml
and
looks like this:
If your Delphi app is published on the Google Play store then the fact that it contains an ARM 7 native library means it won't be available to any Android devices with other CPU hardware and so some of these messages won't be relevant.
However if you distribute the app in an ad hoc manner, say just as a download from your site, then it may get installed on a device with an Intel processor, and so having all these messages available is more important.
These string values will get referenced from the Java code. The point of having
string constants isolated into value files allows easy translation. You can simply
add another file called res\values-fr\strings.xml
and redefine any
or all of the strings in the original file in French and, if the app is run on a
device set to French, the French string will automatically be used by the app.
The Java splash screen activity class is in a file in the project directory called
java\src\com\blong\test\SplashActivity.java
and looks like this:
On creation it hides the Android title bar and goes full screen, then loads up the activity layout resource we saw earlier. Then it starts a thread to run for a few seconds in the background, hopefully giving the native library chance to be copied from the Android package (if necessary) and brought into memory. When the background thread has run for 3 seconds (or the user has touched the splash screen) it then launches the FireMonkey activity to take over and execute the application as usual.
Seeing as this custom Java code will execute before any of the Delphi code can run it seems an ideal place to add in some code that checks out the OS version and the CPU capabilities to ensure they match Delphi's requirements.
Here's my code that does this when the splash activity starts up:
There's nothing much to say about the code in there - just code that looks in the
Android /proc/cpuinfo
file for the relevant indicators and potentially
produces toast pop-up messages using the string value resource IDs. However it neatly
finishes off a Delphi XE5 application, which without any such checks may potentially
fail unpleasantly.
Note that Delphi XE6 will check the processor is ARM 7 and supports NEON instructions in its own Java startup code. However it doesn't do any checks against the running Android version.
In the java
project subdirectory is a batch file called build.bat
.
Before invoking the batch file you must ensure you have added a few directories
from the Android SDK and the JDK to your System path. You can do this either:
PATH
environment variable using steps outlined here or here
SET
commandsSET
commands
Note that the required directories will vary depending on whether you already had
an Android SDK installed, or whether you let Delphi install the Android SDK and, if
so, where you told it to install it. For example you may have already installed
the Android SDK in C:\Android\android-sdk-windows
, or Delphi may have
installed it into C:\Users\Public\Documents\RAD Studio\12.0\PlatformSDKs\adt-bundle-windows-x86-20130522\sdk\android-4.2.2
or you may have given Delphi a specific target directory, meaning the Android SDK
path is in, say, C:\PlatformSDKs\adt-bundle-windows-x86-20130522\sdk\android-4.2.2
.
Directories to add to the path are:
build-tools
directory,
which might be C:\Android\android-sdk-windows\build-tools\18.0.1
or
C:\Users\Public\Documents\RAD Studio\12.0\PlatformSDKs\adt-bundle-windows-x86-20130522\sdk\android-4.2.2
or somewhere else altogether.bin
directory, which will be something along the lines of
C:\Program Files (x86)\Java\jdk1.6.0_23\bin
or C:\Program Files\Java\jdk1.7.0_25\bin
.
I'd certainly recommend extending the Windows search path permanently through the
environment variables option in the system properties dialog so you can run Android
SDK commands and Java commands at any point going forwards, but if you wanted to
run SET
commands at the command prompt or from within the batch file
they might look a little like this:
Once the system path has been updated you will be able to successfully launch the following executables from a command prompt. If you updated the global path then it will be any command prompt launched after you've made the change. If you changed a local environment by running commands at a command prompt then you can run the commands from that command prompt. The commands are:
If you cannot launch any of these commands then go back and review your path settings as they are likely wrong.
To run the build script requires you to first launch a RAD Studio Command Prompt - you can do this in Windows 7 or Windows 8.x by going to the Start menu or Start screen and typing:
RAD Studio
and then selecting the RAD Studio Command Prompt item that is found. Or you can just hunt through the program groups to find it.
Before changing to the directory that contains the batch file using the CD /D
<path_to_batch_file_dir>
command you must open the batch file in
an editor and make appropriate edits to ensure the various environment variables
set at the start are correct. This is what the batch file looks like:
You should modify the set
commands for the following environment variables
and ensure they refer to appropriate exising directories:
ANDROID
- this should point at the Android SDK installation directory,
e.g. C:\Android\android-sdk-windows
or C:\Users\Public\Documents\RAD
Studio\12.0\PlatformSDKs\adt-bundle-windows-x86-20130522\sdk
ANDROID_PLATFORM
- this needs to be set to one of the installed Android
SDK platform directories, found in the Android SDK's platform
directory.
The Delphi installed SDK installs the android-17
platform directory.DX_LIB
- This needs to be set to the directory containing the
dx.jar
Dalvik support library. This is found in the lib
directory
2 levels under the Android SDK's build-tools
directory. You will need
to look to find the correct intermediate directory for your SDK installation. The
batch file suggests this may either be something like android-4.2.2
or something like 18.0.1
.EMBO_DEX
- should point at a classes.dex
file shipped
with Delphi. My installation has one stored as C:\Program Files (x86)\Embarcadero\RAD
Studio\12.0\lib\android\debug\classes.dex
but the environment variable
value is surrounded in quotes due to the spaces in the path.
In the RAD Studio Command Prompt you should change directory to the one containing
the build batch file and then invoke it by running: build
This should proceed to:
dx.bat
Android SDK scriptDexMerger
class in the dx.jar
libraryIf any of these steps fails then you should review the environment variables and see which one has not been set correctly.
In the case that the dx
command fails with the error:
bad class file magic (cafebabe) or version (0033.0000)
then this has a specific cause. You have JDK 1.7 installed (this is what Delphi
will install for you), but dx
expects Java code as compiled by JDK
1.6.
To resolve this issue, you can either switch back to JDK 1.6 or modify the javac.exe command-line to have some additional command-line switches, which force the JDK 1.7 compiler to emit JDK 1.6 compatible Java byte code.
Edit the build script batch file to include this in the javac command-line:
-source 1.6 -target 1.6
Now try and rebuild and the error should not recur.
After a successful build you will have a new classes.dex
file in the
project's java\output\dex
directory, which contains the new splash
screen code in addition to all the regular FMX Java code and Android support library
included there by default.
At last it is time to open the sample project in the Delphi XE5 IDE. The functionality in the application is irrelevant and in this case it is intensely trivial. However we have a little more setup to do in the IDE, so choose the Project, Deployment menu item, which gives us this:
There are a couple of things to note about what is set up here:
classes.dex
file from the RAD Studio directory has been
de-selected. Instead, the newly created classes.dex
in the java\output\dex
directory has been selected to be deployed to the same place: the Android package's
classes
directory. This gets all our splash screen startup Java code
on board within the Android package file.splash_activity.xml
, is set to be deployed
to the res\layout
directory.strings.xml
, is set to be deployed to the
res\values
directory.res\drawable
directory. You can choose whatever splash image you like, but the sample project
uses the launch screen that Delphi XE5 offers for iPad apps: FM_LaunchImagePortrait_768x1004.png
.
You should be aware that the Deployment Manager in Delphi XE5 has a problem with
substituting alternate versions of classes.dex
into Android application
packages. The problem is logged in Quality Central as QC 118472.
The issue manifests itself by Delphi automatically re-selecting the default shipped
classes.dex
when you switch configurations in the Deployment Manager.
This overrides the replacement version and so things very much stop going according
to plan, so keep an eye open for this issue occurring.
In order for the splash screen activity to be launched when Android loads the application package the Android manifest must be updated to reflect this requirement.
When you first compile a Delphi project that is set to target Android, an Android
manifest template is written into the project directory by the name of AndroidManifest.template.xml
.
This is a templatised file that is expanded during compilation cycle into the real
AndroidManifest.xml
file in the project's Android\Debug
or Android\Release
directory.
The Android manifest for a Delphi Android app normally specifies that the FireMonkey
activity, com.embarcadero.firemonkey.FMXNativeActivity
, is the default
activity by virtue of a nested intent filter. The activity in the manifest template
file looks like this by default:
To make the splash screen be the main startup activity, which will launch the FireMonkey activity, this needs to be updated to look as follows. Essentially we have another activity defined, and that new activity takes the intent filter from the FireMonkey activity.
You can see the actvity is defined by its package-qualified class name and is specified to always display in portrait mode.
The application can now be compiled to a native Android library and then deployed onto an Android application package (.apk file) using the Project, Compile SplashScreenTestXE5 and Project, Deploy libSplashScreenTestXE5.so menu items respectively.
If you wish you can skip those two steps and have them done implicitly by choosing
the Run, Run Without Debugging menu item (or by pressing Ctrl+Shift+F9
).
This will uninstall the app if already installed, and then install the new version
onto the target device.
Note that despite choosing the Run Without Debugging menu item, the application
will not be launched on the target device. The same is true if you choose Run, Run
(F9
). This is due to another shortcoming of the IDE in which it assumes
the launch activity will be the FireMonkey activity and so is unable to contend
with the situation we have here where the launch activity has been switched. This
issue is open in Quality Central as QC 118450.
Clearly with the issues with launching the application there is no possibility of debugging your code. You cannot attach to a running Android process as you can with a Windows process, for example.
If you need to actively debug (as opposed to some agricultural equivalent such as
using calls to
Log.d
from the
FMX.Types unit) then it is incumbent
upon you to set up a pair of almost-mirror projects. One project will have all these
hacks described thus far on this page, and will enable a splash screen in your application.
The other project will use the same application logic and units, but will ignore
all aspects of setting up a splash screen. Consequently this second project should
support regular Android app debugging.
Setting up an Android splash screen is feasible. There are three ways to do it and they have various pros and cons. The approach detailed at length on this page is the most cumbersome to set up but offers the most benefits - a splash screen and suitability checking of the OS and CPU as well as familiarity with using custom Java code to achoieve your ends, which can be useful in various areas of Android development with Delphi.
Some aspects of this approach have been slightly simplified in Delphi XE6. An XE6 equivalent of this page is available here.
Go back to the top of this page