Sign in Join
Wiki copytemporary

FPC JVM Android Development: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11*(Android only)*
22
33This page gives you an introduction for developing Android applications using the [[FPC JVM|JVM Port]] of the FPC compiler. For native development for ARM/Android, please see [[Custom Drawn Interface/Android|here]].
44
55## Prerequisites
66
77You need to have the [Android SDK](http://developer.android.com/sdk/index.html) installed (at least version 10 should be sufficient). For more information about installing the SDK please refer to the [Android sites](http://developer.android.com/sdk/installing.html).
88
99You also need the JDK (either the Oracle one or OpenJDK) and the JRE. To be more precise: the applications *jarsigner* and *java* must be available and depending on the method you choose below *javac* needs to be available as well.
1010
1111At last you also need the FPC JVM compiler and the Jasmin assembler for which you should follow the guidelines written [[FPC JVM|here]].
1212
1313## Using Eclipse
1414
1515See <https://macpgmr.github.io/pba/PbaStatus.html>
1616
1717## Using Lazarus
1818
1919### Preparations
2020
2121You should use a trunk version of Lazarus as this already contains a few extensions for language features used and implemented in the JVM port.
2222
2323Also to simplyfy things a bit you might want to patch Lazarus and the compiler driver "fpc":
2424
2525#### Patching Lazarus
2626
2727Apply the following patch to add support for the *jvm* platform and the *android* and *arm* operating systems (this patch is not yet reported, because first the JVM port should be merged back into FPC's trunk):
2828
29-`--- ide/frames/compiler_codegen_options.pas    2011-12-24 17:43:38.000000000 +0000`\
30-`+++ /mnt/data/apps/lazarus/0.9.31/ide/frames/compiler_codegen_options.pas  2011-12-25 18:22:32.593788084+0000`\
31-`@@ -166,6 +166,8 @@`\
32-`       Add('morphos');`\
33-`       Add('embedded');`\
34-`       Add('symbian');`\
35-`+      Add('java');`\
36-`+      Add('android');`\
37-`     end;`\
38-`     ItemIndex := 0;`\
39-`   end;`\
40-`@@ -183,6 +185,7 @@`\
41-`       Add('powerpc');`\
42-`       Add('sparc');`\
43-`       Add('x86_64');`\
44-`+      Add('jvm');`\
45-`     end;`\
46-`     ItemIndex := 0;`\
47-`   end;`
29+``` pascal
30+--- ide/frames/compiler_codegen_options.pas 2011-12-24 17:43:38.000000000 +0000
31++++ /mnt/data/apps/lazarus/0.9.31/ide/frames/compiler_codegen_options.pas 2011-12-25 18:22:32.593788084+0000
32+@@ -166,6 +166,8 @@
33+ Add('morphos');
34+ Add('embedded');
35+ Add('symbian');
36++ Add('java');
37++ Add('android');
38+ end;
39+ ItemIndex := 0;
40+ end;
41+@@ -183,6 +185,7 @@
42+ Add('powerpc');
43+ Add('sparc');
44+ Add('x86_64');
45++ Add('jvm');
46+ end;
47+ ItemIndex := 0;
48+ end;
49+```
4850
4951#### Patching FPC
5052
5153While every platform in Free Pascal has it's own compiler binary (e.g. *ppc386*, *ppcarm*, etc.) there is the binary *fpc* which selects the correct compiler by interpreting the *-Pxxx* argument. To enable support for the *jvm* platform the source code of the *fpc* binary needs to be adjusted. For this you can use any source for this binary that's younger than February 2010 (as this is where the last change was made). You can also simply download the source code file from FPC's trunk ([here](http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/compiler/utils/fpc.pp?view=markup)) for this and compile that as there are no dependencies besides unit *SysUtils*.
5254
5355Here is the patch:
5456
55-`Index: compiler/utils/fpc.pp`\
56-`===================================================================`\
57-`--- compiler/utils/fpc.pp  (Revision 19886)`\
58-`+++ compiler/utils/fpc.pp  (Arbeitskopie)`\
59-`@@ -214,7 +214,9 @@`\
60-`                              cpusuffix:='ia64'`\
61-`                            else if processorstr='x86_64' then`\
62-`                              cpusuffix:='x64'`\
63-`-                           else`\
64-`+                           else if processorstr='jvm' then`\
65-`+                             cpusuffix:='jvm'`\
66-`+                           else                           `\
67-`                              error('Illegal processor type "'+processorstr+'"');`\
68-` `\
69-` {$ifndef darwin}`
57+``` pascal
58+Index: compiler/utils/fpc.pp
59+===================================================================
60+--- compiler/utils/fpc.pp (Revision 19886)
61++++ compiler/utils/fpc.pp (Arbeitskopie)
62+@@ -214,7 +214,9 @@
63+ cpusuffix:='ia64'
64+ else if processorstr='x86_64' then
65+ cpusuffix:='x64'
66+- else
67++ else if processorstr='jvm' then
68++ cpusuffix:='jvm'
69++ else
70+ error('Illegal processor type "'+processorstr+'"');
71+
72+ {$ifndef darwin}
73+```
7074
7175After you compiled the *fpc* binary you can replace your old one with it and copy the *ppcjvm* to the directory where all other compilers are located. These are for the different platforms:
7276
7377- Windows: *%fpcdir%\bin\i386-win32\* (or *x86_64-win64* if you have a native 64-Bit installation)
7478- Linux: *\$fpcdir/lib/fpc/\$fpcversion/* (depending on your installation *\$fpcdir* could be */*, */usr* or some directory in your home directory)
7579- macOS: **WRITE ME**
7680
7781Also you should copy the compiled units from your FPC JVM snapshot to the corresponding *units* directory of your installation:
7882
7983- Windows: from *%fpcjvm%\units\jvm-android* to *%fpcdir%\units\jvm-android*
8084- Linux: from *\$fpcjvm/units/jvm-android* to *\$fpcdir/lib/fpc/\$fpcversion/units/jvm-android*
8185- macOS: **WRITE ME**
8286
8387Also if you don't want to copy the *jasmin.jar* to the same directory as the *ppcjvm*, you should adjust your *fpc.cfg* compiler by adding the following lines:
8488
85-`#IFDEF CPUJVM`\
86-`-FD{Directory of jasmin.jar}`\
87-`#ENDIF`
89+```text
90+#IFDEF CPUJVM
91+-FD{Directory of jasmin.jar}
92+#ENDIF
93+```
8894
8995#### Generate a Key Store
9096
9197Only signed Android packages can be deployed on any Android device (including the emulator) without exception. The only difference to Windows Phone and iOS is that it is sufficient if you sign them yourself (even if you deploy them to the Android market). This is done by creating a keystore which is later utilized by the *jarsigner* tool to sign the package.
9298
9399Using the following command you can create such a key store if you haven't one yet:
94100
95101``` bash
96102keytool -genkey -v -keystore /path/to/YourKeyStore.keystore -alias YourAlias -keyalg RSA -validity 10000
97103```
98104
99105You can freely chose the location and name of the keystore file as well as the alias name. But you need both of the to sign an Android package. The tool will then ask you some questions and for the passwords for keystore and alias (you can use the same password for both or different ones).
100106
101107*Note:* You should also follow the guidelines written on the [Android developer sites](http://developer.android.com/guide/publishing/app-signing.html) so that your application might be approved for the Android market.
102108
103109### Structure of an Android application
104110
105111An Android applications consists of the source code of the applications, some resources like images and XML files which describe the layout of the GUI (similar to LFMs) and a manifest file which describes options like the minimum SDK version or which activity is the main one.
106112
107113This leads to another difference to e.g. Lazarus applications: an Android applications consists of at least one so called *Activity*, which can be thought of as a combination of Lazarus' *TForm* and *TApplication*. Every activity that can be started must be defined in the manifest file (otherwise an exception occurs if one tries to start that activity). Activities are started using so called *intents* which tell the activity why they are started and are also used to send parameters to the activity. Thus the main project file of an Android is not a program file, but an unit which will contain at least one class that derives from *android.app.Activity* or one of its subclasses (like *android.app.ListActivity*). An activity then consists of a view which contain - depending on their class - one or more additional views which all together make up the GUI of an Android application. These views can either be instantiated at runtime by creating the view classes or by *inflating* a layout that is part of the resources. Handling events (e.g. reacting on the click of a button) is always done by specifying a class instance that implements the corresponding listener interface (Java does not support procedure/method variables).
108114
109115### Building an Android application
110116
111117#### Summary
112118
113119For building an Android applications multiple tools are necessary. First the resources need to be converted to binaries and added to a "resource package". Also the IDs they have in this resource package need to be added to a resource class (usally called *R*). Then the source code needs to be compiled (in our case using the FPC JVM compiler). After that the generated *.class files need to be converted to the Dalvik's DEX format and combined with the resource package and the manifest these build a unsigned package (extension APK). This package then needs to be signed (using *jarsigner*) and can then be installed on the emulator or a device or (hopefully) be deployed to the Android market.
114120
115121#### Used Directories
116122
117123To simplyfy the given commandlines the following conventions are used:
118124
119125- *\$platform* gives the *platforms/android-VERSION/* directory of the Android SDK
120126- *\$platformtools* gives the *platform-tools* directory of the Android SDK
121127- *\$tools* gives the *tools* directory of the Android SDK
122128
123129#### Directory layout
124130
125131To avoid clutter in the project's directory the following directory layout is suggested:
126132
127-`-\`\
128-` |- bin (will contain the package files and the *.dex file)`\
129-` |  |`\
130-` |  \- classes (contains the compiled *.class files and FPC's PPU files`\
131-` |`\
132-` |- gen (this will contain the generated R.java file for the resources)`\
133-` |`\
134-` |- res (here the resources will be located)`\
135-` |  |`\
136-` |  |- drawable(-hdpi/-ldpi/-mdpi) (directories containing icons, images, etc)`\
137-` |  |`\
138-` |  |- layout (layouts for the GUI)`\
139-` |  |`\
140-` |  |- menu (layouts for the menus (context menu, options menu))`\
141-` |  |`\
142-` |  \- values (containing for example strings used by the application)`\
143-` |`\
144-` \- src (the source code of your application)`
133+```text
134+-\
135+ |- bin (will contain the package files and the *.dex file)
136+ | |
137+ | \- classes (contains the compiled *.class files and FPC's PPU files
138+ |
139+ |- gen (this will contain the generated R.java file for the resources)
140+ |
141+ |- res (here the resources will be located)
142+ | |
143+ | |- drawable(-hdpi/-ldpi/-mdpi) (directories containing icons, images, etc)
144+ | |
145+ | |- layout (layouts for the GUI)
146+ | |
147+ | |- menu (layouts for the menus (context menu, options menu))
148+ | |
149+ | \- values (containing for example strings used by the application)
150+ |
151+ \- src (the source code of your application)
152+```
145153
146154#### The manifest file
147155
148156A basic manifest file looks like the following:
149157
150158``` xml
151159<?xml version="1.0" encoding="utf-8"?>
152160<manifest xmlns:android="http://schemas.android.com/apk/res/android"
153161 package="org.myname.myapp"
154162 android:versionCode="1"
155163 android:versionName="1.0">
156164 <uses-sdk
157165 android:minSdkVersion="9"
158166 android:targetSdkVersion="9" />
159167 <application android:label="@string/app_name" android:icon="@drawable/icon">
160168 <activity android:name="TMyAppActivity"
161169 android:label="@string/app_name">
162170 <intent-filter>
163171 <action android:name="android.intent.action.MAIN" />
164172 <category android:name="android.intent.category.LAUNCHER" />
165173 </intent-filter>
166174 </activity>
167175 <activity android:name="TSomeOtherActivity" />
168176 </application>
169177</manifest>
170178```
171179
172180The attribute *package* gives the class path of your application. This will be used by the generation of the *R.java* and you should also use this at least for the *\$namespace* option in units containing activities. The attributes containing "@" are references to resources in the *res* directory. Hereby the *drawable* is replaced by *drawable-mdpi*, *drawable-hdpi* or *drawable-ldpi* depending of the screen size at runtime. The value *@string/app_name* refers to the string *app_name* in the *res/values/strings.xml* file. *TMyAppActivity* is the main activity which will be invoked when started by the launcher. *TSomeOtherActivity* is for example some second application window. Both need to derive from *android.app.Activity* or one of its subclasses. Having *targetSdkVersion* and *minSdkVersion* set will avoid that compatibilty modules are used if you have a newer SDK version installed as well.
173181
174182#### The resource files
175183
176184The exact contents of your resource directory will depend on your application, but some generic advices can be given:
177185
178186The *drawable* directories can contain a *icon.png* in different sizes (ldpi: 36x36, mdpi: 48x48, hdpi: 72x72).
179187
180188For the contents of the layouts in the *menu* and *layout* directories I advice you to read the [Android documentation](http://developer.android.com/guide/topics/ui/declaring-layout.html).
181189
182190The *values* directory contains a *strings.xml* which looks like the following:
183191
184192``` xml
185193<?xml version="1.0" encoding="utf-8"?>
186194<resources>
187195 <string name="app_name">My App</string>
188196</resources>
189197```
190198
191199#### Generate the resource package
192200
193201To generate the resource package and the R.java you can use the following command:
194202
195203``` bash
196204$platformtools/aapt p -f -M AndroidManifest.xml -F bin/myapp.ap_ -I $platform/android.jar -S res -m -J gen
197205```
198206
199207This will take all resources from the directory given by *-S*, the manifest file given by *-M* and the *android.jar* given by *-I* to generate the resource package given by *-F* and the *R.java* of which the base directory is given by *-J* (the remaining class path is extracted from the manifest).
200208
201209#### The resource identifiers
202210
203211The generated *R.java* file (located in *gen/org/myname/myapp* according to above shown manifest file) will look like the following (supposing that there is a layout for a dialog in *res/layouts*, two layouts for menus in *res/menu* and two more strings in *res/values/strings.xml*):
204212
205213``` java
206214/* AUTO-GENERATED FILE. DO NOT MODIFY.
207215*
208216* This class was automatically generated by the
209217* aapt tool from the resource data it found. It
210218* should not be modified by hand.
211219*/
212220
213221package org.myname.myapp;
214222
215223public final class R {
216224 public static final class attr {
217225 }
218226 public static final class drawable {
219227 public static final int icon=0x7f020000;
220228 }
221229 public static final class id {
222230 public static final int dialog_name_cancel=0x7f060002;
223231 public static final int dialog_name_name=0x7f060000;
224232 public static final int dialog_name_ok=0x7f060001;
225233 }
226234 public static final class layout {
227235 public static final int dialog_name=0x7f030000;
228236 }
229237 public static final class menu {
230238 public static final int myapp_context=0x7f050000;
231239 public static final int myapp_options=0x7f050001;
232240 }
233241 public static final class string {
234242 public static final int app_name=0x7f040000;
235243 public static final int dialog_name_cancel=0x7f040001;
236244 public static final int dialog_name_ok=0x7f040002;
237245 }
238246}
239247```
240248
241249This class then needs to be converted to Pascal code in a unit I decided to call *src/resources.pas* (this needs to be done everytime you recreate the resources, because ID values might change then). The unit then looks like this:
242250
243251``` pascal
244252unit Resources;
245253
246254{$mode objfpc}{$H+}
247255{$modeswitch unicodestrings}
248256{$namespace org.myname.myapp}
249257
250258interface
251259
252260type
253261 R = class sealed
254262 public
255263 type
256264 attr = class sealed
257265
258266 end;
259267
260268 drawable = class sealed
261269 public
262270 const
263271 icon = $7f020000;
264272 end;
265273
266274 id = class sealed
267275 public
268276 const
269277 dialog_name_cancel = $7f060002;
270278 dialog_name_name = $7f060000;
271279 dialog_name_ok = $7f060001;
272280 end;
273281
274282 layout = class sealed
275283 public
276284 const
277285 dialog_name = $7f030000;
278286 end;
279287
280288 menu = class sealed
281289 public
282290 const
283291 myapp_context = $7f050000;
284292 myapp_options = $7f050001;
285293 end;
286294
287295 strings = class sealed
288296 public
289297 const
290298 app_name = $7f040000;
291299 dialog_name_cancel = $7f040001;
292300 dialog_name_ok = $7f040002;
293301 end;
294302 end;
295303
296304implementation
297305
298306end.
299307```
300308
301309You could also use global constants, but this way it looks more like the usual Android development.
302310
303311#### Compiling the source
304312
305313If you've followed the advice above regarding adjusting FPC and Lazarus you should be able to simply compile the code using Lazarus and setting the target to *jvm-android*. The unit output directory should also be set to *bin/classes*.
306314
307315If you did not follow the advice you can create a script or a makefile containg the following command (note: you can not simply change the compiler, because Lazarus then does not know the target "android"):
308316
309317``` bash
310318/path/to/ppcjvm -n -Tandroid -Fu/path/to/android-rtl -FEbin/classes -FD/directory/of/jasmin-jar myapp.lpr
311319```
312320
313321You can then add this script, makefile or also the command itself to Lazarus' pre or post build commands (Project =\> Project Settings =\> Compilation) and uncheck all checkboxes for the compilation command. For error locations you can also check the "FPC" checkbox next to "look for messages" for your command.
314322
315323After the source is compiled you need to convert it to the format understood by Dalvik. For this you also need to copy all *.class files provided in the *units/jvm-android* directory and subdirectories to the *bin/classes* directory (it should be possible to put the RTL files into a JAR file as well, but this was not tested by me). Then you simply execute the following command:
316324
317325``` bash
318326$platformtools/dx --dex --output=bin/classes.dex bin/classes
319327```
320328
321329#### Generating the package
322330
323331Now you need to generate first a unsigned package out of the resources and the dex file. This is done using the following command:
324332
325333``` bash
326334$tools/apkbuilder bin/myapp-unsigned.apk -u -z bin/myapp.ap_ -f myapp/classes.dex
327335```
328336
329337The file given with *-z* is the resource package generated earlier.
330338
331339*Note:* This tools will print a message that it is deprecated (which is true), but for now it still works. Until it is removed a solution for this needs to be found (maybe by developing a custom application around the Java class which does the hard work and which is not deprecated).
332340
333341After the unsigned package is created it needs to be signed so it can be loaded to the emulator or device. For this the following command suffices:
334342
335343``` bash
336344jarsigner -keystore /path/to/YourKeyStore.keystore -signedjar bin/myapp.apk bin/myapp-unsigned.apk YourAlias
337345```
338346
339347The values */path/to/YourKeyStore.keystore* and *YourAlias* are the same which you created earlier. You are then prompted for the passwords of the two.
340348
341349#### Deploying the package
342350
343351Using the *adb* tool the package can be directly installed to a running emulator or connected device (for more infos on the latter see [here](http://developer.android.com/guide/developing/device.html)).
344352
345353For the emulator use the following command:
346354
347355``` bash
348356$tools/adb -e install -r bin/myapp.apk
349357```
350358
351359For the device the following:
352360
353361``` bash
354362$tools/adb -d install -r bin/myapp.apk
355363```
356364
357365In both cases the *-r* tells *adb* to reinstall the application (thus application data like databases is kept).
358366
359367You can uninstall an application (together with its user data) using the following command:
360368
361369``` bash
362370$tools/adb -e uninstall org.myname.myapp
363371```
364372
365373Use *-d* instead of *-e* if you want to do this on a connected device.
366374
367375*Hint:* The uninstall command gives a good reason why you should use different namespaces for your application.
368376
369377### Finding errors
370378
371379During the development of an Android application you might often see a dialog that informs you that your application has crashed. You can then use the *\$tools/adb* tool with the "logcat" command which will cat the log of the device or emulator to your console (you need to use Ctrl+C to close it again). There you'll see the name of the Exception class, its message and a stacktrace. You can also use the tool *\$tools/ddms* which is a GUI application and represents the log to you in a colored way (depending on the severity of the log message). It also allows taking screenshots.
372380
373381You can also write log messages to that log yourself by using the static methods of the class *android.utils.Log*.
374382
375383Common errors:
376384
377385- using a functionality that is not part of the running version: the current Android unit is generated for SDK version 14. As the compiler does not know what e.g. version 10 might have contained or not contained you are responsible yourself that you only use methods and classes that are available in the corresponding SDK version. Possible exceptions are "FieldNotFoundException" or "ClassDefNotFoundException"
378386
379387---
380388*From the Free Pascal wiki, page "FPC JVM Android Development", as it stood on 2020-08-03 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
381389Category: Android
382390Category: JVM

Back to history