services.add(new ServiceStartApp(this));
Castle Game Engine defines a number of useful Android services and iOS services to integrate with various 3rd-party libraries, SDKs to provide additional functionality to your application.
This page documents how you can add new services for Android.
The final Android project has it’s own activity class (MainActivity
, descendant of NativeActivity
), and it has Java code created by combining the files from:
Subdirectories of tools/build-tool/data/android/integrated-services for all requested services.
It’s easy to add your own Android services, if you need to integrate with some Android library not covered by existing Android services. Create your service by adding new directory under tools/build-tool/data/android/integrated-services. The files there are copied to the final Android project, expanding macros inside. Most of the files are simply copied (but avoiding overwriting), but some files are merged in a special way:
AndroidManifest.xml
of each service is merged with the main AndroidManifest.xml
in a smart way, only adding the new permissions and new elements/attributes inside the
build.gradle
files inside the services have a special XML syntax. See the example services like helpshift
. They are merged into the final (non-XML) build.gradle
file in a smart way.
Each service may also insert a snippet of Java code to the MainActivity.java
file. This is typically used to initialize the service-specific Java class, like
services.add(new ServiceStartApp(this));
When developing new services, it’s useful to inspect the generated Android project in the castle-engine-output/android/project
. This is created by the build tool by merging all the services with base project template. Look there after running castle-engine package --target=android
.
The main code of a service is usually a class descending from the ServiceAbstract
class. It has some methods that you can override, like onCreate
, onStart
, and so on — they are called when the appropriate lifecycle event occurs on the MainActivity
. The idea is that our MainActivity
should be a relatively small class, that simply "passes on" the events to every service.
To communicate with the services from the Object Pascal code, use the CastleMessaging
unit. Our units like CastleAds
or CastleGameService
are simply thin "wrappers" using CastleMessaging
unit under the hood — see at their sources to know how it works, it is very straightfoward.
This way you can write Java code and utilize any Android Java API, and communicate with Pascal.
The CastleMessaging
is a simple asynchronous communication mechanism between Pascal and Java, using it is easy (you have ready methods on both Pascal and Java side, and you don’t need to deal with JNI).
On the Pascal side, use CastleMessaging
unit, that exposes a singleton Messaging
.
Use Messaging.Send(['message-1','some-parameter'])
to send something.
Register your callback like Messaging.OnReceive.Add(@MessageReceived)
to receive something.
Typically, you wrap sending and receiving messages in a nice Pascal API. Examples of it are in the CGE src/services/
directory.
On the Java side, you create a class descending from ServiceAbstract
. Such class can override messageReceived
method to receive messages, and use messageSend
to send messages. Examples of it are CGE services inside tools/build-tool/data/android/integrated-services/XXX/src/io/castleengine/ServiceXXX.java
files.
See e.g. Google Play Games handling:
the Pascal part is in src/services/castlegameservices.pas
,
the Java part is in tools/build-tool/data/android/integrated-services/google_play_games/src/io/castleengine/ServiceGooglePlayGames.java
.
The Java code of this particular service is no longer straightforward (it has a lot of functionality by now…), but it shows how to pass messages back and forth.
For something simpler, see the "vibrate" service:
the Java side is in build-tool/data/android/integrated-services/vibrate/
.
and the Pascal side is a trivial function Vibrate
.
Note that CastleMessaging
is not supposed to be an "API for all communication", but in practice it works very well in all existing services — since it’s natural for asynchronous processing (where many things are done in some thread, and/or wait for network or user input before returning any result).
The exact messages are not documented anywhere, since they are the "internal" API between Pascal and Java code. You should just cross-reference the castlegameservices.pas
and ServiceGooglePlayGames.java
and see that what one sends — the other receives:) When creating new service, you just invent your own messages, using any non-conflicting names.
In the Java part, you use the full power of Java APIs on Android, that are necessary to access various Android stuff. On the Pascal side, you create a thin wrappers that merely send/receive messages. Final games should use the Pascal API, without being aware of any "messages" underneath, e.g. see how castle-engine/examples/2d_dragon_spine_android_game/
uses the TGameService
class.
Note
|
The disadvantage of CastleMessaging approach is that the communication between Java and Pascal looks like an asynchronous network channel. That’s OK in many cases (especially when the Java code indeed wraps some network operations), but it may be bad if you need to get something fast/synchronously. For such cases, using JNI to implement (possibly bidirectional) communication between Java and Pascal is necessary.
|
A service can add a dynamic library (SO, file like libxxx.so
) to your Android project.
The library should be compiled for all possible Android architectures. At least provide versions for these 2 CPUs:
Arm 32-bit (called just arm
by various tools)
Arm 64-bit (called aarch64
by various tools).
Moreover, it’s best to provide versions for "normal" (typically used on desktop PCs) CPUs, they will be useful in emulators and virtual machines:
x86
(typical 32-bit PCs; also called i386
by some tools)
x86_64
(typical 64-bit PCs; also called amd64
by some tools)
In theory, you don’t need to provide all versions, you don’t even need to provide versions for both 32-bit and 64-bit Arm. But then your application will only support one Android architecture. You would then have to build the application like
castle-engine --cpu=arm --os=android
(to make 32-bit-only application)
or castle-engine --cpu=aarch64 --os=android
(to make 64-bit-only application).
We strongly advise that you provide all SO versions, and compile your Android applications for all common architectures using simple castle-engine --target=android
. This compiles for both 32-bit Arm and 64-bit Arm now. For people testing in emulator, we recommend to explicitly build with x86_64 CPU now.
Example services that includes SO files see:
They contain just a few trivial files, and the SO files for all architectures.
A service can include C or C++ code that will be automatically compiled for all suitable Android architectures using CMake system automatically invoked by Android Gradle.
If your service is a library implemented using C++ code, we recommend to put C++ source code in the service (not a precompiled library) to avoid having to deal with complications of compiling C++ code for Android, linking with proper base C++ library etc. Let the Android Gradle do the job for you.
For services using plain C, it may be that this is also the easiest approach.
Examples of using this approach:
sound (using OpenAL Soft)
internal_test_cpp_lib (test dummy C++ library)
To improve this documentation just edit this page and create a pull request to cge-www repository.