Python For Android
Python For Android
Release 0.1
Alexander Taylor
1 Contents 3
1.1 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Build options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3 Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4 Working on Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.5 Launcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.6 distutils/setuptools integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.7 Recipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.8 Bootstraps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
1.9 Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.10 Troubleshooting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
1.11 Docker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
1.12 Development and Contributing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
1.13 Testing an python-for-android pull request . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Index 49
i
ii
python-for-android Documentation, Release 0.1
python-for-android is an open source build tool to let you package Python code into standalone android APKs. These
can be passed around, installed, or uploaded to marketplaces such as the Play Store just like any other Android
app. This tool was originally developed for the Kivy cross-platform graphical framework, but now supports multiple
bootstraps and can be easily extended to package other types of Python apps for Android.
python-for-android supports two major operations; first, it can compile the Python interpreter, its dependencies, back-
end libraries and python code for Android devices. This stage is fully customisable: you can install as many or few
components as you like. The result is a standalone Android project which can be used to generate any number of
different APKs, even with different names, icons, Python code etc. The second function of python-for-android is to
provide a simple interface to these distributions, to generate from such a project a Python APK with build parameters
and Python code to taste.
Contents 1
python-for-android Documentation, Release 0.1
2 Contents
CHAPTER 1
Contents
Getting up and running on python-for-android (p4a) is a simple process and should only take you a couple of minutes.
We’ll refer to Python for android as p4a in this documentation.
1.1.1 Concepts
Basic:
• requirements: For p4a, all your app’s dependencies must be specified via --requirements similar to the
standard requirements.txt. (Unless you specify them via a setup.py/install_requires) All dependencies will be
mapped to “recipes” if any exist, so that many common libraries will just work. See “recipe” below for details.
• distribution: A distribution is the final “build” of your compiled project + requirements, as an Android project
assembled by p4a that can be turned directly into an APK. p4a can contain multiple distributions with different
sets of requirements.
• build: A build refers to a compiled recipe or distribution.
• bootstrap: A bootstrap is the app backend that will start your application. The default for graphical applications
is SDL2. You can also use e.g. the webview for web apps, or service_only/service_library for background
services. Different bootstraps have different additional build options.
Advanced:
• recipe: A recipe is a file telling p4a how to install a requirement that isn’t by default fully Android compatible.
This is often necessary for Cython or C/C++-using python extensions. p4a has recipes for many common
libraries already included, and any dependency you specified will be automatically mapped to its recipe. If a
dependency doesn’t work and has no recipe included in p4a, then it may need one to work.
1.1.2 Installation
3
python-for-android Documentation, Release 0.1
Installing p4a
You can also test the master branch from Github using:
Installing Dependencies
On Arch Linux you should be able to run the following to install most of the dependencies (note: this list may not be
complete):
4 Chapter 1. Contents
python-for-android Documentation, Release 0.1
On macOS:
brew install autoconf automake libtool openssl pkg-config
brew tap homebrew/cask-versions
brew install --cask homebrew/cask-versions/adoptopenjdk8
Warning: python-for-android is often picky about the SDK/NDK versions. Pick the recommended ones from
below to avoid problems.
You need to download and unpack the Android SDK and NDK to a directory (let’s say $HOME/Documents/):
• Android SDK
• Android NDK
For the Android SDK, you can download ‘just the command line tools’. When you have extracted these you’ll see
only a directory named tools, and you will need to run extra commands to install the SDK packages needed.
For Android NDK, note that modern releases will only work on a 64-bit operating system. The minimal, and recom-
mended, NDK version to use is r25b:
• Go to ndk downloads page
• Windows users should create a virtual machine with an GNU Linux os installed, and then you can follow the
described instructions from within your virtual machine.
First, install an API platform to target. The recommended *target* API level is 27, you can replace it with a
different number but keep in mind other API versions are less well-tested and older devices are still supported down
to the recommended specified *minimum* API/NDK API level 21:
$SDK_DIR/tools/bin/sdkmanager "platforms;android-27"
Second, install the build-tools. You can use $SDK_DIR/tools/bin/sdkmanager --list to see all the possi-
bilities, but 28.0.2 is the latest version at the time of writing:
$SDK_DIR/tools/bin/sdkmanager "build-tools;28.0.2"
Then, you can edit your ~/.bashrc or other favorite shell to include new environment variables necessary for
building on android:
# Adjust the paths!
export ANDROIDSDK="$HOME/Documents/android-sdk-27"
export ANDROIDNDK="$HOME/Documents/android-ndk-r23b"
export ANDROIDAPI="27" # Target API version of your application
(continues on next page)
You have the possibility to configure on any command the PATH to the SDK, NDK and Android API using:
• --sdk-dir PATH as an equivalent of $ANDROIDSDK
• --ndk-dir PATH as an equivalent of $ANDROIDNDK
• --android-api VERSION as an equivalent of $ANDROIDAPI
• --ndk-api VERSION as an equivalent of $NDKAPI
• --ndk-version VERSION as an equivalent of $ANDROIDNDKVER
1.1.3 Usage
To build your application, you need to specify name, version, a package identifier, the bootstrap you want to use (sdl2
for kivy or sdl2 apps) and the requirements:
Note on --requirements: you must add all libraries/dependencies your app needs to run. Example:
--requirements=python3,kivy,vispy. For an SDL2 app, kivy is not needed, but you need to add any
wrappers you might use (e.g. pysdl2).
This p4a apk . . . command builds a distribution with python3, kivy, and everything else you specified in the require-
ments. It will be packaged using a SDL2 bootstrap, and produce an .apk file.
Compatibility notes:
• Python 2 is no longer supported by python-for-android. The last release supporting Python 2 was v2019.10.06.
To build your application, you need to have a name, version, a package identifier, and explicitly use the webview
bootstrap, as well as the requirements:
Please note as with kivy/SDL2, you need to specify all your additional requirements/dependencies.
You can also replace flask with another web framework.
Replace --port=5000 with the port on which your app will serve a website. The default for Flask is 5000.
To build an android archive (.aar), containing an android service , you need a name, version, package identifier,
explicitly use the service_library bootstrap, and declare service entry point (See services for more options), as well as
the requirements and arch(s):
6 Chapter 1. Contents
python-for-android Documentation, Release 0.1
You can then call the generated Java entrypoint(s) for your Python service(s) in other apk build frameworks.
Exporting the Android App Bundle (aab) for distributing it on Google Play
Starting from August 2021 for new apps and from November 2021 for updates to existings apps, Google Play Console
will require the Android App Bundle instead of the long lived apk.
python-for-android handles by itself the needed work to accomplish the new requirements:
˓→arch=armeabi-v7a --release
This p4a aab . . . command builds a distribution with python3, kivy, and everything else you specified in the re-
quirements. It will be packaged using a SDL2 bootstrap, and produce an .aab file that contains binaries for both
armeabi-v7a and arm64-v8a ABIs.
The Android App Bundle, is supposed to be used for distributing your app. If you need to test it locally, on your
device, you can use bundletool <https://developer.android.com/studio/command-line/bundletool>
Other options
You can pass other command line arguments to control app behaviours such as orientation, wakelock and app permis-
sions. See Bootstrap options.
Rebuild everything
If anything goes wrong and you want to clean the downloads and builds to retry everything, run:
p4a clean_all
If you just want to clean the builds to avoid redownloading dependencies, run:
Getting help
If something goes wrong and you don’t know how to fix it, add the --debug option and post the output log to the
kivy-users Google group or the kivy #support Discord channel.
See Troubleshooting for more information.
Recipe management
p4a recipes
If you are contributing to p4a and want to test a recipes again, you need to clean the build and rebuild your distribution:
You can write “private” recipes for your application, just create a p4a-recipes folder in your build directory, and
place a recipe in it (edit the __init__.py):
mkdir -p p4a-recipes/myrecipe
touch p4a-recipes/myrecipe/__init__.py
Distribution management
Every time you start a new project, python-for-android will internally create a new distribution (an Android build
project including Python and your other dependencies compiled for Android), according to the requirements you
added on the command line. You can force the reuse of an existing distribution by adding:
This will ensure your distribution will always be built in the same directory, and avoids using more disk space every
time you adjust a requirement.
You can list the available distributions:
p4a distributions
p4a clean_dists
Configuration file
python-for-android checks in the current directory for a configuration file named .p4a. If found, it adds all the lines
as options to the command line. For example, you can add the options you would always include such as:
--dist_name my_example
--android_api 27
--requirements kivy,openssl
You can override the source of any recipe using the $P4A_recipename_DIR environment variable. For instance,
to test your own Kivy branch you might set:
export P4A_kivy_DIR=/home/username/kivy
The specified directory will be copied into python-for-android instead of downloading from the normal url specified
in the recipe.
8 Chapter 1. Contents
python-for-android Documentation, Release 0.1
If your application is also packaged for desktop using setup.py, you may want to use your setup.py instead of the
--requirements option to avoid specifying things twice. For that purpose, check out distutils/setuptools integra-
tion
Going further
See the other pages of this doc for more information on specific topics:
• Build options
• Commands
• Recipes
• Bootstraps
• Working on Android
• Troubleshooting
• Launcher
• Development and Contributing
python-for-android supports using Python 3.8 or higher. To explicitly select a Python version in your requirements,
use e.g. --requirements=python3==3.10.11,hostpython3==3.10.11.
The last python-for-android version supporting Python2 was v2019.10.06
Python-for-android no longer supports building for Python 3 using the CrystaX NDK. The last python-for-android
version supporting CrystaX was 0.7.0
python-for-android supports multiple app backends with different types of interface. These are called bootstraps.
Currently the following bootstraps are supported, but we hope that it should be easy to add others if your project has
different requirements. Let us know if you’d like help adding a new one.
sdl2
Use this with --bootstrap=sdl2, or just include the sdl2 recipe, e.g. --requirements=sdl2,python3.
SDL2 is a popular cross-platform depelopment library, particularly for games. It has its own Android project support,
which python-for-android uses as a bootstrap, and to which it adds the Python build and JNI code to start it.
From the point of view of a Python program, SDL2 should behave as normal. For instance, you can build apps with
Kivy or PySDL2 and have them work with this bootstrap. It should also be possible to use e.g. pygame_sdl2, but this
would need a build recipe and doesn’t yet have one.
Build options
The sdl2 bootstrap supports the following additional command line options (this list may not be exhaustive):
• --private: The directory containing your project files.
• --package: The Java package name for your project. e.g. org.example.yourapp.
• --name: The app name.
• --version: The version number.
• --orientation: The orientations that the app will display in. (Available options are
portrait, landscape, portrait-reverse, landscape-reverse). Since Android ignores
android:screenOrientation when in multi-window mode (Which is the default on Android 12+), this
option will also set the window orientation hints for the SDL bootstrap. If multiple orientations are given,
android:screenOrientation will be set to unspecified. - --manifest-orientation: The orienta-
tion that will be set for the android:screenOrientation
attribute of the activity in the AndroidManifest.xml file. If not set, the value will be
synthesized from the --orientation option. The full list of valid options is given under
android:screenOrientation in the Android documentation.
• --icon: A path to the png file to use as the application icon.
• --permission: A permission that needs to be declared into the App AndroidManifest.xml. For mul-
tiple permissions, add multiple --permission arguments. --home-app Gives you the option to set your
application as a home app (launcher) on your Android device.
Warning: The syntax --permission VIBRATE (only the permission name, without the prefix), is also
supported for backward compatibility, but it will be removed in the future.
10 Chapter 1. Contents
python-for-android Documentation, Release 0.1
• --window: If the argument is included, the application will not cover the Android status bar.
• --blacklist: The path to a file containing blacklisted patterns that will be excluded from the final APK.
Defaults to ./blacklist.txt.
• --whitelist: The path to a file containing whitelisted patterns that will be included in the APK even if also
blacklisted.
• --add-jar: The path to a .jar file to include in the APK. To include multiple jar files, pass this argument
multiple times.
• --intent-filters: A file path containing intent filter xml to be included in AndroidManifest.xml.
• --service: A service name and the Python script it should run. See Arbitrary service scripts.
• --add-source: Add a source directory to the app’s Java code.
• --no-byte-compile-python: Skip byte compile for .py files.
• --enable-androidx: Enable AndroidX support library.
• --add-resource: Put this file or directory in the apk res directory.
webview
You can use this with --bootstrap=webview, or include the webviewjni recipe, e.g.
--requirements=webviewjni,python3.
The webview bootstrap gui is, per the name, a WebView displaying a webpage, but this page is hosted on the device
via a Python webserver. For instance, your Python code can start a Flask application, and your app will display and
allow the user to navigate this website.
Note: Your Flask script must start the webserver without :code:debug=True. Debug mode doesn’t seem to work
on Android due to use of a subprocess.
This bootstrap will automatically try to load a website on port 5000 (the default for Flask), or you can specify a
different option with the –port command line option. If the webserver is not immediately present (e.g. during the short
Python loading time when first started), it will instead display a loading screen until the server is ready.
• --private: The directory containing your project files.
• --package: The Java package name for your project. e.g. org.example.yourapp.
• --name: The app name.
• --version: The version number.
• --orientation: The orientations that the app will display in. (Available options are
portrait, landscape, portrait-reverse, landscape-reverse). Since Android ignores
android:screenOrientation when in multi-window mode (Which is the default on Android 12+), this
setting is not guaranteed to work, and you should consider to implement a custom orientation change handler
in your app.
• --manifest-orientation: The orientation that will be set in the android:screenOrientation
attribute of the activity in the AndroidManifest.xml file. If not set, the value will be synthesized from the
--orientation option. The full list of valid options is given under android:screenOrientation in
the Android documentation.
• --icon: A path to the png file to use as the application icon.
• --permission: A permission name for the app, e.g. --permission VIBRATE. For multiple permis-
sions, add multiple --permission arguments.
• --meta-data: Custom key=value pairs to add in the application metadata.
• --presplash: A path to the image file to use as a screen while the application is loading.
• --presplash-color: The presplash screen background color, of the form #RRGGBB or a color name red,
green, blue etc.
• --wakelock: If the argument is included, the application will prevent the device from sleeping.
• --window: If the argument is included, the application will not cover the Android status bar.
• --blacklist: The path to a file containing blacklisted patterns that will be excluded from the final APK.
Defaults to ./blacklist.txt.
• --whitelist: The path to a file containing whitelisted patterns that will be included in the APK even if also
blacklisted.
• --add-jar: The path to a .jar file to include in the APK. To include multiple jar files, pass this argument
multiple times.
• --intent-filters: A file path containing intent filter xml to be included in AndroidManifest.xml.
• --service: A service name and the Python script it should run. See Arbitrary service scripts.
• add-source: Add a source directory to the app’s Java code.
• --port: The port on localhost that the WebView will access. Defaults to 5000.
service_library
12 Chapter 1. Contents
python-for-android Documentation, Release 0.1
To optimize the size of the .apk file that p4a builds for you, you can blacklist certain core components. Per default,
p4a will add python with batteries included as would be expected on desktop, including openssl, sqlite3 and other
components you may not use.
To blacklist an item, specify the --blacklist-requirements option:
p4a apk ... --blacklist-requirements=sqlite3
At the moment, the following core components can be blacklisted (if you don’t want to use them) to decrease APK
size:
• android disables p4a’s android module (see android for Android API access)
• libffi disables ctypes stdlib module
• openssl disables ssl stdlib module
• sqlite3 disables sqlite3 stdlib module
1.3 Commands
This page documents all the commands and options that can be passed to toolchain.py.
The commands available are the methods of the ToolchainCL class, documented below. They may have options of
their own, or you can always pass general arguments or distribution arguments to any command (though if irrelevant
they may not have an effect).
These arguments may be passed to any command in order to modify its behaviour, though not all commands make use
of them.
--debug Print extra debug information about the build, including all compilation output.
--sdk_dir The filepath where the Android SDK is installed. This can alternatively be set in several other ways.
--android_api The Android API level to target; python-for-android will check if the platform tools for this level
are installed.
--ndk_dir The filepath where the Android NDK is installed. This can alternatively be set in several other ways.
--ndk_version The version of the NDK installed, important because the internal filepaths to build tools depend
on this. This can alternatively be set in several other ways, or if your NDK dir contains a RELEASE.TXT
containing the version this is automatically checked so you don’t need to manually set it.
p4a supports several arguments used for specifying which compiled Android distribution you want to use. You may
pass any of these arguments to any command, and if a distribution is required they will be used to load, or compile, or
download this as necessary.
None of these options are essential, and in principle you need only supply those that you need.
1.3. Commands 13
python-for-android Documentation, Release 0.1
--name NAME The name of the distribution. Only one distribution with a given name can be created.
--requirements LIST,OF,REQUIREMENTS The recipes that your distribution must contain, as a comma sep-
arated list. These must be names of recipes or the pypi names of Python modules.
--force-build BOOL Whether the distribution must be compiled from scratch.
--arch The architecture to build for. You can specify multiple architectures to build for at the same time. As
an example p4a ... --arch arm64-v8a --arch armeabi-v7a ... will build a distribution for
both arm64-v8a and armeabi-v7a.
--bootstrap BOOTSTRAP The Java bootstrap to use for your application. You mostly don’t need to worry about
this or set it manually, as an appropriate bootstrap will be chosen from your --requirements. Current
choices are sdl2 (used with Kivy and most other apps) or webview.
Note: These options are preliminary. Others will include toggles for allowing downloads, and setting additional
directories from which to load user dists.
This page gives details on accessing Android APIs and managing other interactions on Android.
If you want to store and retrieve data, you shouldn’t just save to the current directory, and not hardcode /sdcard/ or
some other path either - it might differ per device.
Instead, the android module which you can add to your –requirements allows you to query the most commonly required
paths:
app_storage_path() gives you Android’s so-called “internal storage” which is specific to your app and cannot seen by
others or the user. It compares best to the AppData directory on Windows.
primary_external_storage_path() returns Android’s so-called “primary external storage”, often found at /sdcard/ and
potentially accessible to any other app. It compares best to the Documents directory on Windows. Requires Permis-
sion.WRITE_EXTERNAL_STORAGE to read and write to.
secondary_external_storage_path() returns Android’s so-called “secondary external storage”, often found at /stor-
age/External_SD/. It compares best to an external disk plugged to a Desktop PC, and can after a device restart become
inaccessible if removed. Requires Permission.WRITE_EXTERNAL_STORAGE to read and write to.
Warning: Even if secondary_external_storage_path returns a path the external sd card may still not be present.
Only non-empty contents or a successful write indicate that it is.
14 Chapter 1. Contents
python-for-android Documentation, Release 0.1
Read more on all the different storage types and what to use them for in the Android documentation:
https://developer.android.com/training/data-storage/files
A note on permissions
Only the internal storage is always accessible with no additional permissions. For both primary and secondary external
storage, you need to obtain Permission.WRITE_EXTERNAL_STORAGE and the user may deny it. Also, if you get
it, both forms of external storage may only allow your app to write to the common pre-existing folders like “Music”,
“Documents”, and so on. (see the Android Docs linked above for details)
With API level >= 21, you will need to request runtime permissions to access the SD card, the camera, and other
things.
This can be done through the android module which is available per default unless you blacklist it. Use it in your app
like this:
With the SDL2 bootstrap, the app’s splash screen may be visible longer than necessary (with your app already being
loaded) due to a limitation with the way we check if the app has properly started. In this case, the splash screen
overlaps the app gui for a short time.
To dismiss the loading screen explicitly in your code, use the android module:
You can call it e.g. using kivy.clock.Clock.schedule_once to run it in the first active frame of your app, or
use the app build method.
Android phones always have a back button, which users expect to perform an appropriate in-app function. If you do
not handle it, Kivy apps will actually shut down and appear to have crashed.
In SDL2 bootstraps, the back button appears as the escape key (keycode 27, codepoint 270). You can handle this key
to perform actions when it is pressed.
For instance, in your App class in Kivy:
class YourApp(App):
def build(self):
Window.bind(on_keyboard=self.key_input)
return Widget() # your root widget here as normal
When the user leaves an App, it is automatically paused by Android, although it gets a few seconds to store data etc.
if necessary. Once paused, there is no guarantee that your app will run again.
With Kivy, add an on_pause method to your App class, which returns True:
def on_pause(self):
return True
The default PythonActivity has a observer pattern for onActivityResult and onNewIntent.
android.activity.bind(eventname=callback, ...)
This allows you to bind a callback to an Android event: - on_new_intent is the event associated to the
onNewIntent java call - on_activity_result is the event associated to the onActivityResult java call
Warning: This method is not thread-safe. Call it in the mainthread of your app. (tips: use
kivy.clock.mainthread decorator)
android.activity.unbind(eventname=callback, ...)
Unregister a previously registered callback with bind().
Example:
# This example is a snippet from an NFC p2p app implemented with Kivy.
16 Chapter 1. Contents
python-for-android Documentation, Release 0.1
def nfc_enable(self):
activity.bind(on_new_intent=self.on_new_intent)
# ...
def nfc_disable(self):
activity.unbind(on_new_intent=self.on_new_intent)
# ...
The Android Application class provides the ActivityLifecycleCallbacks interface where callbacks can be regis-
tered corresponding to activity lifecycle changes. These callbacks can be used to implement logic in the Python app
when the activity changes lifecycle states.
Note that some of the callbacks are not useful in the Python app. For example, an onActivityCreated callback will
never be run since the the activity’s onCreate callback will complete before the Python app is running. Similarly,
saving instance state in an onActivitySaveInstanceState callback will not be helpful since the Python app doesn’t have
access to the restored instance state.
android.activity.register_activity_lifecycle_callbacks(callbackname=callback, ...)
This allows you to bind a callbacks to Activity lifecycle state changes. The callback names correspond to
ActivityLifecycleCallbacks method names such as onActivityStarted. See the ActivityLife-
cycleCallbacks documentation for names and function signatures for the callbacks.
android.activity.unregister_activity_lifecycle_callbacks(instance)
Unregister a ActivityLifecycleCallbacks instance previously registered with
register_activity_lifecycle_callbacks().
Example:
def on_activity_stopped(activity):
print('Activity is stopping')
register_activity_lifecycle_callbacks(
onActivityStopped=on_activity_stopped,
)
Implementation of the android BroadcastReceiver. You can specify the callback that will receive the broadcast event,
and actions or categories filters.
class android.broadcast.BroadcastReceiver
Warning: The callback will be called in another thread than the main thread. In that thread, be careful not
to access OpenGL or something like that.
# In java: Intent.ACTION_HEADSET_PLUG
# In python: 'headset_plug'
start()
Register the receiver with all the actions and categories, and start handling events.
stop()
Unregister the receiver with all the actions and categories, and stop handling events.
Example:
class TestApp(App):
def build(self):
self.br = BroadcastReceiver(
self.on_broadcast, actions=['headset_plug'])
self.br.start()
# ...
# Don't forget to stop and restart the receiver when the app is going
# to pause / resume mode
def on_pause(self):
self.br.stop()
return True
def on_resume(self):
self.br.start()
Runnable
Runnable is a wrapper around the Java Runnable class. This class can be used to schedule a call of a Python function
into the PythonActivity thread.
18 Chapter 1. Contents
python-for-android Documentation, Release 0.1
Example:
def helloworld(arg):
print 'Called from PythonActivity with arg:', arg
Runnable(helloworld)('hello')
@run_on_ui_thread
def helloworld(arg):
print 'Called from PythonActivity with arg:', arg
helloworld('arg1')
Warning: Because the python function is called from the PythonActivity thread, you need to be careful about
your own calls.
As mentioned above, the android Python module provides a simple wrapper around many native Android APIS,
and it is included by default unless you blacklist it.
The available functionality of this module is not separately documented. You can read the source on Github.
Also please note you can replicate most functionality without it using pyjnius. (see below)
Plyer provides a more thorough wrapper than android for a much larger area of platform-specific APIs, supporting not
only Android but also iOS and desktop operating systems. (Though plyer is a work in progress and not all platforms
support all Plyer calls yet)
Plyer does not support all APIs yet, but you can always use Pyjnius to call anything that is currently missing.
You can include Plyer in your APKs by adding the Plyer recipe to your build requirements, e.g.
--requirements=plyer.
You should check the Plyer documentation for details of all supported facades (platform APIs), but as an example the
following is how you would achieve vibration as described in the Pyjnius section above:
Pyjnius lets you call the Android API directly from Python Pyjnius is works by dynamically wrapping Java classes, so
you don’t have to wait for any particular feature to be pre-supported.
This is particularly useful when android and plyer don’t already provide a convenient access to the API, or you need
more control.
You can include Pyjnius in your APKs by adding pyjnius to your build requirements, e.g.
--requirements=flask,pyjnius. It is automatically included in any APK containing Kivy, in which
case you don’t need to specify it manually.
The basic mechanism of Pyjnius is the autoclass command, which wraps a Java class. For instance, here is the code
to vibrate your device:
from jnius import autoclass
activity = PythonActivity.mActivity
Context = autoclass('android.content.Context')
vibrator = activity.getSystemService(Context.VIBRATOR_SERVICE)
1.5 Launcher
The Kivy Launcher is an Android application that can run any Kivy app stored in the kivy folder on the SD Card. You
can download the latest stable version for your android device from the Play Store.
The stable launcher comes with various Python packages and permissions, usually listed in the description in the store.
Those aren’t always enough for an application to run or even launch if you work with other dependencies that are not
packaged.
20 Chapter 1. Contents
python-for-android Documentation, Release 0.1
The Kivy Launcher is intended for quick and simple testing. For anything more advanced we recommend building
your own APK with python-for-android.
1.5.1 Building
The Kivy Launcher is built using python-for-android. To get the most recent versions of packages you need to clean
them first, so that the packager won’t grab an old (cached) package instead of a fresh one.
Note: –minsdk 13 is necessary for the new toolchain, otherwise you’ll be able to run apps only in landscape orienta-
tion.
Warning: Do not use any of –private, –public, –dir or other arguments for adding main.py or main.pyc to the
app. The argument –launcher is above them and tells the p4a to build the launcher version of the APK.
1.5.2 Usage
Once the launcher is installed, you need to create a folder in your external storage directory (e.g. /storage/
emulated/0 or /sdcard) - this is normally your ‘home’ directory in a file browser. Each new folder inside kivy
represents a separate application:
/sdcard/kivy/<yourapplication>
Each application folder must contain an android.txt file. The file has to contain three basic lines:
title=<Application Title>
author=<Your Name>
orientation=<portrait|landscape>
The file is editable so you can change for example orientation or name. These are the only options dynamically
configurable here, although when the app runs you can call the Android API with PyJNIus to change other settings.
After you set your android.txt file, you can now run the launcher and start any available app from the list.
To differentiate between apps in /sdcard/kivy, you can include an icon named icon.png in the folder. The icon
should be a square.
1.5. Launcher 21
python-for-android Documentation, Release 0.1
Launcher is released on Google Play with each new Kivy stable branch. The master branch is not suitable for a regular
user because it changes quickly and needs testing.
If you feel confident, feel free to improve the launcher. You can find the source code at |renpy|_ or at |kivy|_.
If your project has a setup.py file, then it can be executed by p4a when your app is packaged such that your app
properly ends up in the packaged site-packages. (Use --use-setup-py to enable this, --ignore-setup-py
to prevent it)
This is functionality to run setup.py INSIDE ‘p4a apk‘, as opposed to the other section below, which is about running
p4a inside setup.py.
This however has these caveats:
• Only your ‘‘main.py‘‘ from your app’s ‘‘–private‘‘ data is copied into the .apk! Everything else needs to
be installed by your setup.py into the site-packages, or it won’t be packaged.
• All dependencies that map to recipes can only be pinned to exact versions, all other constraints will either just
plain not work or even cause build errors. (Sorry, our internal processing is just not smart enough to honor them
properly at this point)
• The dependency analysis at the start may be quite slow and delay your build
Reasons why you would want to use a setup.py to be processed (and omit specifying --requirements):
• You want to use a more standard mechanism to specify dependencies instead of --requirements
• You already use a setup.py for other platforms
• Your application imports itself in a way that won’t work unless installed to site-packages)
Reasons not to use a setup.py (that is to use the usual --requirements mechanism instead):
• You don’t use a setup.py yet, and prefer the simplicity of just specifying --requirements
• Your setup.py assumes a desktop platform and pulls in Android-incompatible dependencies, and you are not
willing to change this, or you want to keep it separate from Android deployment for other organizational reasons
• You need data files to be around that aren’t installed by your setup.py into the site-packages folder
Instead of running p4a via the command line, you can call it via setup.py instead, by it integrating with distutils and
setup.py.
This is functionality to run p4a INSIDE setup.py, as opposed to the other section above, which is about running
setup.py inside ‘p4a apk‘.
The base command is:
22 Chapter 1. Contents
python-for-android Documentation, Release 0.1
The files included in the APK will be all those specified in the package_data argument to setup. For instance, the
following example will include all .py and .png files in the testapp folder:
setup(
name='testapp_setup',
version='1.1',
description='p4a setup.py example',
author='Your Name',
author_email='[email protected]',
packages=find_packages(),
options=options,
package_data={'testapp': ['*.py', '*.png']}
)
The app name and version will also be read automatically from the setup.py.
The Android package name uses org.test.lowercaseappname if not set explicitly.
The --private argument is set automatically using the package_data. You should not set this manually.
The target architecture defaults to --armeabi.
All of these automatic arguments can be overridden by passing them manually on the command line, e.g.:
Instead of providing extra arguments on the command line, you can store them in setup.py by passing the options
parameter to setup. For instance:
options = {'apk': {'debug': None, # use None for arguments that don't pass a value
'requirements': 'sdl2,pyjnius,kivy,python3',
'android-api': 19,
'ndk-dir': '/path/to/ndk',
'dist-name': 'bdisttest',
}}
packages = find_packages()
print('packages are', packages)
setup(
name='testapp_setup',
version='1.1',
description='p4a setup.py example',
author='Your Name',
author_email='[email protected]',
packages=find_packages(),
options=options,
(continues on next page)
These options will be automatically included when you run python setup.py apk. Any options passed on the
command line will override these values.
You can also provide p4a arguments in the setup.cfg file, as normal for distutils. The syntax is:
[apk]
argument=value
requirements=sdl2,kivy
1.7 Recipes
This page describes how python-for-android (p4a) compilation recipes work, and how to build your own. If you just
want to build an APK, ignore this and jump straight to the Getting Started.
Recipes are special scripts for compiling and installing different programs (including Python modules) into a p4a
distribution. They are necessary to take care of compilation for any compiled components, as these must be compiled
for Android with the correct architecture.
python-for-android comes with many recipes for popular modules. No recipe is necessary for Python modules which
have no compiled components; these are installed automatically via pip. If you are new to building recipes, it is
recommended that you first read all of this page, at least up to the Recipe reference documentation. The different
recipe sections include a number of examples of how recipes are built or overridden for specific purposes.
The formal reference documentation of the Recipe class can be found in the Recipe class section and below.
Check the recipe template section for a template that combines all of these ideas, in which you can replace whichever
components you like.
The basic declaration of a recipe is as follows:
class YourRecipe(Recipe):
url = 'http://example.com/example-{version}.tar.gz'
version = '2.0.3'
md5sum = '4f3dc9a9d857734a488bcbefd9cd64ed'
recipe = YourRecipe()
24 Chapter 1. Contents
python-for-android Documentation, Release 0.1
See the Recipe class documentation for full information about each parameter.
These core options are vital for all recipes, though the url may be omitted if the source is somehow loaded from
elsewhere.
You must include recipe = YourRecipe(). This variable is accessed when the recipe is imported.
Note: The url includes the {version} tag. You should only access the url with the versioned_url property,
which replaces this with the version attribute.
The actual build process takes place via three core methods:
These methods are always run in the listed order; prebuild, then build, then postbuild.
If you defined a url for your recipe, you do not need to manually download it, this is handled automatically.
The recipe will automatically be built in a special isolated build directory, which you can access with self.
get_build_dir(arch.arch). You should only work within this directory. It may be convenient to use the
current_directory context manager defined in toolchain.py:
The argument to each method, arch, is an object relating to the architecture currently being built for. You can mostly
ignore it, though may need to use the arch name arch.arch.
Note: You can also implement arch-specific versions of each method, which are called (if they exist) by the superclass,
e.g. def prebuild_armeabi(self, arch).
This is the core of what’s necessary to write a recipe, but has not covered any of the details of how one actually writes
code to compile for android. This is covered in the next sections, including the standard mechanisms used as part of
the build, and the details of specific recipe classes for Python, Cython, and some generic compiled recipes. If your
module is one of the latter, you should use these later classes rather than reimplementing the functionality from scratch.
You can easily apply patches to your recipes by adding them to the patches declaration, e.g.:
1.7. Recipes 25
python-for-android Documentation, Release 0.1
patches = ['some_fix.patch',
'another_fix.patch']
The paths should be relative to the recipe file. Patches are automatically applied just once (i.e. not reapplied the second
time python-for-android is run).
You can also use the helper functions in pythonforandroid.patching to apply patches depending on certain
conditions, e.g.:
...
class YourRecipe(Recipe):
...
You can include your own conditions by passing any function as the second entry of the tuple. It will receive the arch
(e.g. x86, armeabi) and recipe (i.e. the Recipe object) as kwargs. The patch will be applied only if the function
returns True.
Installing libs
Some recipes generate .so files that must be manually copied into the android project. You can use code like the
following to accomplish this, copying to the correct lib cache dir:
import shutil
shutil.copyfile('a_generated_binary.so',
self.ctx.get_libs_dir(arch.arch))
Any libs copied to this dir will automatically be included in the appropriate libs dir of the generated android project.
When performing any compilation, it is vital to do so with appropriate environment variables set, ensuring that the
Android libraries are properly linked and the compilation target is the correct architecture.
You can get a dictionary of appropriate environment variables with the get_recipe_env method. You should make
sure to set this environment for any processes that you call. It is convenient to do this using the sh module as follows:
You can also use the shprint helper function from the p4a toolchain module, which will print information about the
process and its current status:
26 Chapter 1. Contents
python-for-android Documentation, Release 0.1
You can also override the get_recipe_env method to add new env vars for use in your recipe. For instance, the
Kivy recipe does the following when compiling for SDL2, in order to tell Kivy what backend to use:
env['KIVY_SDL2_PATH'] = ':'.join([
join(self.ctx.bootstrap.build_dir, 'jni', 'SDL', 'include'),
join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_image'),
join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_mixer'),
join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_ttf'),
])
return env
Warning: When using the sh module like this the new env completely replaces the normal environment, so you
must define any env vars you want to access.
The Recipe class has a should_build method, which returns a boolean. This is called for each architecture before
running build_arch, and if it returns False then the build is skipped. This is useful to avoid building a recipe more
than once for different dists.
By default, should_build returns True, but you can override it however you like. For instance, PythonRecipe and its
subclasses all replace it with a check for whether the recipe is already installed in the Python distribution:
If your recipe is to install a Python module without compiled components, you should use a PythonRecipe. This
overrides build_arch to automatically call the normal python setup.py install with an appropriate en-
vironment.
For instance, the following is all that’s necessary to create a recipe for the Vispy module:
1.7. Recipes 27
python-for-android Documentation, Release 0.1
site_packages_name = 'vispy'
recipe = VispyRecipe()
The site_packages_name is a new attribute that identifies the folder in which the module will be installed in the
Python package. This is only essential to add if the name is different to the recipe name. It is used to check if the
recipe installation can be skipped, which is the case if the folder is already present in the Python installation.
For reference, the code that accomplishes this is the following:
def install_python_package(self):
'''Automate the installation of a Python package (or a cython
package where the cython components are pre-built).'''
arch = self.filtered_archs[0]
env = self.get_recipe_env(arch)
with current_directory(self.get_build_dir(arch.arch)):
hostpython = sh.Command(self.ctx.hostpython)
This combines techniques and tools from the above documentation to create a generic mechanism for all Python
modules.
Note: The hostpython is the path to the Python binary that should be used for any kind of installation. You must run
Python in a similar way if you need to do so in any of your own recipes.
If your recipe is to install a Python module that uses Cython, you should use a CythonRecipe. This overrides
build_arch to both build the cython components and to install the Python module just like a normal PythonRecipe.
For instance, the following is all that’s necessary to make a recipe for Kivy:
class KivyRecipe(CythonRecipe):
version = 'stable'
url = 'https://github.com/kivy/kivy/archive/{version}.zip'
name = 'kivy'
recipe = KivyRecipe()
28 Chapter 1. Contents
python-for-android Documentation, Release 0.1
The failing build and manual cythonisation is necessary, firstly to make sure that any .pyx files have been generated by
setup.py, and secondly because cython isn’t installed in the hostpython build.
This may actually fail if the setup.py tries to import cython before making any .pyx files (in which case it crashes too
early), although this is probably not usually an issue. If this happens to you, try patching to remove this import or
make it fail quietly.
Other than this, these methods follow the techniques in the above documentation to make a generic recipe for most
cython based modules.
This is similar to a CythonRecipe but is intended for modules like numpy which include compiled but non-cython
components. It uses a similar mechanism to compile with the right environment.
This isn’t documented yet because it will probably be changed so that CythonRecipe inherits from it (to avoid code
duplication).
If you are writing a recipe not for a Python module but for something that would normally go in the JNI dir of an
Android project (i.e. it has an Application.mk and Android.mk that the Android build system can use), you
can use an NDKRecipe to automatically set it up. The NDKRecipe overrides the normal get_build_dir method
to place things in the Android project.
1.7. Recipes 29
python-for-android Documentation, Release 0.1
Warning: The NDKRecipe does not currently actually call ndk-build, you must add this call (for your module)
by manually making a build_arch method. This may be fixed later.
For instance, the following recipe is all that’s necessary to place SDL2_ttf in the jni dir. This is built later by the SDL2
recipe, which calls ndk-build with this as a dependency:
class LibSDL2TTF(NDKRecipe):
version = '2.0.12'
url = 'https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-{version}.tar.gz'
dir_name = 'SDL2_ttf'
recipe = LibSDL2TTF()
The dir_name argument is a new class attribute that tells the recipe what the jni dir folder name should be. If it is
omitted, the recipe name is used. Be careful here, sometimes the folder name is important, especially if this folder is
a dependency of something else.
The following template includes all the recipe sections you might use. None are compulsory, feel free to delete method
overrides if you do not use them:
from pythonforandroid.toolchain import Recipe, shprint, current_directory
from os.path import exists, join
import sh
import glob
class YourRecipe(Recipe):
# This could also inherit from PythonRecipe etc. if you want to
# use their pre-written build processes
version = 'some_version_string'
url = 'http://example.com/example-{version}.tar.gz'
# {version} will be replaced with self.version when downloading
30 Chapter 1. Contents
python-for-android Documentation, Release 0.1
recipe = YourRecipe()
This documentation covers most of what is ever necessary to make a recipe work. For further examples, python-for-
android includes many recipes for popular modules, which are an excellent resource to find out how to add your own.
You can find these in the python-for-android Github page.
The Recipe is the base class for all p4a recipes. The core documentation of this class is given below, followed by
discussion of how to create your own Recipe subclass.
1.8 Bootstraps
This page is about creating new bootstrap backends. For build options of existing bootstraps (i.e. with SDL2, Webview,
etc.), see build options.
python-for-android (p4a) supports multiple bootstraps. These fulfill a similar role to recipes, but instead of describing
how to compile a specific module they describe how a full Android project may be put together from a combination of
individual recipes and other components such as Android source code and various build files.
This page describes the basics of how bootstraps work so that you can create and use your own if you like, making it
easy to build new kinds of Python projects for Android.
A bootstrap class consists of just a few basic components, though one of them must do a lot of work.
For instance, the SDL2 bootstrap looks like the following:
from pythonforandroid.toolchain import Bootstrap, shprint, current_directory, info,
˓→warning, ArchAndroid, logger, info_main, which
1.8. Bootstraps 31
python-for-android Documentation, Release 0.1
class SDL2Bootstrap(Bootstrap):
name = 'sdl2'
recipe_depends = ['sdl2']
def run_distribute(self):
# much work is done here...
The declaration of the bootstrap name and recipe dependencies should be clear. However, the run_distribute
method must do all the work of creating a build directory, copying recipes etc into it, and adding or removing any extra
components as necessary.
If you’d like to create a bootstrap, the best resource is to check the existing ones in the p4a source code. You can also
contact the developers if you have problems or questions.
1.9 Services
python-for-android supports the use of Android Services, background tasks running in separate processes. These are
the closest Android equivalent to multiprocessing on e.g. desktop platforms, and it is not possible to use normal
multiprocessing on Android. Services are also the only way to run code when your app is not currently opened by the
user.
Services must be declared when building your APK. Each one will have its own main.py file with the Python script to
be run. Please note that python-for-android explicitly runs services as separated processes by having a colon “:” in the
beginning of the name assigned to the android:process attribute of the AndroidManifest.xml file. This
is not the default behavior, see Android service documentation. You can communicate with the service process from
your app using e.g. osc or (a heavier option) twisted.
Service folder
This is the older method of handling services. It is recommended to use the second method (below) where possible.
Create a folder named service in your app directory, and add a file service/main.py. This file should contain
the Python code that you want the service to run.
To start the service, use the start_service function from the android module (you may need to add android
to your app requirements):
import android
android.start_service(title='service name',
description='service description',
arg='argument to service')
32 Chapter 1. Contents
python-for-android Documentation, Release 0.1
This method is recommended for non-trivial use of services as it is more flexible, supporting multiple services and a
wider range of options.
To create the service, create a python script with your service code and add a
--service=myservice:PATH_TO_SERVICE_PY argument when calling python-for-android, or in buil-
dozer.spec, a services = myservice:PATH_TO_SERVICE_PY [app] setting.
The myservice name before the colon is the name of the service class, via which you will interact with it later.
The PATH_TO_SERVICE_PY is the relative path to the service entry point (like services/myservice.py)
You can optionally specify the following parameters:
• :foreground for launching a service as an Android foreground service
• :sticky for launching a service that gets restarted by the Android OS on exit/error
Full command with all the optional parameters included would be: --service=myservice:services/
myservice.py:foreground:sticky
You can add multiple --service arguments to include multiple services, or separate them with a comma in buil-
dozer.spec, all of which you will later be able to stop and start from your app.
To run the services (i.e. starting them from within your main app code), you must use PyJNIus to interact with the java
class python-for-android creates for each one, as follows:
To customize the notification icon, title, and text use three optional arguments to service.start():
Where ‘small_icon’ is the name of an Android drawable or mipmap resource, and ‘title’ and ‘content’ are strings in
the notification.
Services support a range of options and interactions not yet documented here but all accessible via calling other
methods of the service reference.
1.9. Services 33
python-for-android Documentation, Release 0.1
Note: The app root directory for Python imports will be in the app root folder even if the service file is in a subfolder.
To import from your service folder you must use e.g. import service.module instead of import module,
if the service file is in the service/ folder.
Service auto-restart
It is possible to make services restart automatically when they exit by calling setAutoRestartService(True)
on the service object. The call to this method should be done within the service code:
1.10 Troubleshooting
Add the --debug option to any python-for-android command to see full debug output including the output of all the
external tools used in the compilation and packaging steps.
If reporting a problem by email or Discord, it is usually helpful to include this full log, e.g. via a pastebin or Github
gist.
python-for-android is managed by the Kivy Organisation, and you can get help with any problems using the same
channels as Kivy itself:
• by email to the kivy-users Google group
• on #support Discord channel
If you find a bug, you can also post an issue on the python-for-android Github page.
When a python-for-android APK doesn’t work, often the only indication that you get is that it closes. It is important
to be able to find out what went wrong.
python-for-android redirects Python’s stdout and stderr to the Android logcat stream. You can see this by enabling
developer mode on your Android device, enabling adb on the device, connecting it to your PC (you should see a
notification that USB debugging is connected) and running adb logcat. If adb is not in your PATH, you can
find it at /path/to/Android/SDK/platform-tools/adb, or access it through python-for-android with the
shortcut:
python-for-android logcat
or:
34 Chapter 1. Contents
python-for-android Documentation, Release 0.1
Running logcat command gives a lot of information about what Android is doing. You can usually see important lines
by using logcat’s built in functionality to see only lines with the python tag (or just grepping this).
When your app crashes, you’ll see the normal Python traceback here, as well as the output of any print statements etc.
that your app runs. Use these to diagnose the problem just as normal.
The adb command passes its arguments straight to adb itself, so you can also do other debugging tasks such as
python-for-android adb devices to get the list of connected devices.
For further information, see the Android docs on adb, and on logcat in particular.
It is sometimes useful to unpack a packaged APK to see what is inside, especially when debugging python-for-android
itself.
APKs are just zip files, so you can extract the contents easily:
unzip YourApk.apk
At the top level, this will always contain the same set of files:
$ ls
AndroidManifest.xml classes.dex META-INF res
assets lib YourApk.apk resources.arsc
The user app data (code, images, fonts ..) is packaged into a single tarball contained in the assets folder:
$ cd assets
$ ls
private.tar
$ tar xf private.tar
This will reveal all the user app data (the files shown below are from the touchtracer demo):
$ ls
README.txt android.txt icon.png main.pyc
˓→ p4a_env_vars.txt particle.png
private.tar touchtracer.kv
Due to how We’re required to ship ABI-specific things in Android App Bundle, the Python installation is packaged
separately, as (most of it) is ABI-specific.
For example, the Python installation for arm64-v8a is available in lib/arm64-v8a/libpybundle.so
libpybundle.so is a tarball (but named like a library for packaging requirements), that contains our
_python_bundle:
$ tar xf libpybundle.so
$ cd _python_bundle
$ ls
modules site-packages stdlib.zip
1.10. Troubleshooting 35
python-for-android Documentation, Release 0.1
The following are common problems and resolutions that users have reported.
This occurs if your version of colorama is too low, install version 0.3.3 or higher.
If you install python-for-android with pip or via setup.py, this dependency should be taken care of automatically.
This is a known bug in some releases. To work around it, add your python requirement explicitly, e.g.
--requirements=python3,kivy. This also applies when using buildozer, in which case add python3 to your
buildozer.spec requirements.
This can happen when you try to include a very long filename, which doesn’t normally happen but can occur acci-
dentally if the p4a directory contains a .buildozer directory that is not excluded from the build (e.g. if buildozer was
previously used). Removing this directory should fix the problem, and is desirable anyway since you don’t want it in
the APK.
Requested API target 19 is not available, install it with the SDK android tool
This means that your SDK is missing the required platform tools. You need to install the platforms;android-19
package in your SDK, using the android or sdkmanager tools (depending on SDK version).
If using buildozer this should be done automatically, but as a workaround you can run these from ~/.buildozer/
android/platform/android-sdk-20/tools/android.
You do not have the libffi headers available to python-for-android, so you need to install them. On Ubuntu and
derivatives these come from the libffi-dev package.
After installing the headers, clean the build (p4a clean builds, or with buildozer delete the .buildozer directory within
your app directory) and run python-for-android again.
SSLError(“Can’t connect to HTTPS URL because the SSL module is not available.”)
Your hostpython3 was compiled without SSL support. You need to install the SSL development files before rebuilding
the hostpython3 recipe. Remember to always clean the build before rebuilding (p4a clean builds, or with buildozer
buildozer android clean).
On Ubuntu and derivatives:
On macOS:
36 Chapter 1. Contents
python-for-android Documentation, Release 0.1
1.11 Docker
Currently we use a containerized build for testing Python for Android recipes. Docker supports three big platforms
either directly with the kernel or via using headless VirtualBox and a small distro to run itself on.
While this is not the actively supported way to build applications, if you are willing to play with the approach, you can
use the Dockerfile to build the Docker image we use for CI builds and create an Android application with that in
a container. This approach allows you to build Android applications on all platforms Docker engine supports. These
steps assume you already have Docker preinstalled and set up.
Warning: This approach is highly space unfriendly! The more layers (commit) or even Docker images (build)
you create the more space it’ll consume. Within the Docker image there is Android SDK and NDK + various
dependencies. Within the custom diff made by building the distribution there is another big chunk of space eaten.
The very basic stuff such as a distribution with: CPython 3, setuptools, Python for Android android module,
SDL2 (+ deps), PyJNIus and Kivy takes almost 2 GB. Check your free space first!
Note: You need to be in the python-for-android for the Docker build context and you can optionally
use --file flag to specify the path to the Dockerfile location.
3. Create a container from p4a image with copied testapps folder in the image mounted to the same one in the
cloned repo on the host:
docker run \
--interactive \
--tty \
--volume ".../testapps":/home/user/testapps \
p4a sh -c
'. venv/bin/activate \
&& cd testapps \
&& python setup_testapp_python3.py apk \
--sdk-dir $ANDROID_SDK_HOME \
--ndk-dir $ANDROID_NDK_HOME'
Note: On Windows you might need to use quotes and forward-slash path for volume “/c/Users/. . . /python-for-
android/testapps”:/home/user/testapps
1.11. Docker 37
python-for-android Documentation, Release 0.1
Warning: On Windows gradlew will attempt to use ‘bashr’ command which is a result of Windows line
endings. For that you’ll need to install dos2unix package into the image.
ls -lah testapps
1.12.2 Versioning
python-for-android releases currently use calendar versioning. Release numbers are of the form YYYY.MM.DD. We
aim to create a new release every four weeks, but more frequent releases are also possible.
We use calendar versioning because in practice, changes in python-for-android are often driven by updates or adjust-
ments in the Android build tools. It’s usually best for users to be working from the latest release. We try to maintain
backwards compatibility even while internals are changing.
38 Chapter 1. Contents
python-for-android Documentation, Release 0.1
Release checklist
- [ ] Build (or download from github actions) and run the following [testapps](https:/
˓→/github.com/kivy/python-for-android/tree/master/testapps/on_device_unit_tests) for
- [ ] on_device_unit_tests
- [ ] `armeabi-v7a` (`cd testapps/on_device_unit_tests && PYTHONPATH=.:../../
˓→python3 setup.py apk --ndk-dir=<your-ndk-dir> --sdk-dir=<your-sdk-dir> --
˓→arch=armeabi-v7a --debug`)
Basic concepts
(This part repeats other parts of the docs, for the sake of making this a more independent read)
p4a builds & packages a python application for use on Android. It does this by providing a Java wrapper, and for
graphical applications an SDL2-based wrapper which can be used with the kivy UI toolkit if desired (or alternatively
just plain PySDL2). Any such python application will of course have further library dependencies to do its work.
p4a supports two types of package dependencies for a project:
Recipe: install script in custom p4a format. Can either install C/C++ or other things that cannot be pulled in via pip,
or things that can be installed via pip but break on android by default. These are maintained primarily inside the p4a
source tree by p4a contributors and interested folks.
Python package: any random pip python package can be directly installed if it doesn’t need adjustments to work for
Android.
p4a will map any dependency to an internal recipe if present, and otherwise use pip to obtain it regularly from whatever
external source.
The install/build process of a p4a project, as triggered by the p4a apk command, roughly works as follows in regards
to python packages:
1. The user has specified a project folder to install. This is either just a folder with python scripts and a main.py,
or it may also have a pyproject.toml for a more standardized install.
2. Dependencies are collected: they can be either specified via --requirements as a list of names or pip-style
URLs, or p4a can optionally scan them from a project folder via the pep517 library (if there is a pyproject.toml
or setup.py).
3. The collected dependencies are mapped to p4a’s recipes if any are available for them, otherwise they’re kept
around as external regular package references.
4. All the dependencies mapped to recipes are built via p4a’s internal mechanisms to build these recipes. (This
may or may not indirectly use pip, depending on whether the recipe wraps a python package or not and uses pip
to install or not.)
5. If the user has specified to install the project in standardized ways, then the setup.py/whatever build sys-
tem of the project will be run. This happens with cross compilation set up (CC/CFLAGS/. . . set to use the
proper toolchain) and a custom site-packages location. The actual comand is a simple pip install . in the
project folder with some extra options: e.g. all dependencies that were already installed by recipes will be
pinned with a -c constraints file to make sure pip won’t install them, and build isolation will be disabled via
--no-build-isolation so pip doesn’t reinstall recipe-packages on its own.
If the user has not specified to use standardized build approaches, p4a will simply install all the remaining
dependencies that weren’t mapped to recipes directly and just plain copy in the user project without installing.
Any setup.py or pyproject.toml of the user project will then be ignored in this step.
6. Google’s gradle is invoked to package it all up into an .apk.
Here are some common things worth knowing about python-for-android’s dealing with python packages:
• Packages will work fine without a recipe if they would also build on Linux ARM, don’t use any API not available
in the NDK if they use native code, and don’t use any weird compiler flags the toolchain doesn’t like if they use
native code. The package also needs to work with cross compilation.
• There is currently no easy way for a package to know it is being cross-compiled (at least that we know of) other
than examining the CC compiler that was set, or that it is being cross-compiled for Android specifically. If that
breaks a package it currently needs to be worked around with a recipe.
• If a package does not work, p4a developers will often create a recipe instead of getting upstream to fix it because
p4a simply is too niche.
• Most packages without native code will just work out of the box. Many with native code tend not to, especially
if complex, e.g. numpy.
• Anything mapped to a p4a recipe cannot be just reinstalled by pip, specifically also not inside build isolation as
a dependency. (It may work if the patches of the recipe are just relevant to fix runtime issues.) Therefore as of
now, the best way to deal with this limitation seems to be to keep build isolation always off.
40 Chapter 1. Contents
python-for-android Documentation, Release 0.1
• We in overall prefer to use the recipe mechanism less if we can. In overall the recipes are just a collection of
workarounds. It may look quite hacky from the outside, since p4a version pins recipe-wrapped packages usually
to make the patches reliably apply. This creates work for the recipes to be kept up-to-date, and obviously this
approach doesn’t scale too well. However, it has ended up as a quite practical interims solution until better ways
are found.
• Obviously, it would be nice if packages could know they are being cross-compiled, and for Android specifically.
We aren’t currently aware of a good mechanism for that.
• If pip could actually run the recipes (instead of p4a wrapping pip and doing so) then this might even allow build
isolation to work - but this might be too complex to get working. It might be more practical to just gradually
reduce the reliance on recipes instead and make more packages work out of the box. This has been done e.g.
with improvements to the cross-compile environment being set up automatically, and we’re open for any ideas
on how to improve this.
The first step to do it’s to get a copy of the pull request, we can do it of several ways, and that it will depend of the
circumstances but all the methods presented here will do the job, so. . .
For the example, we will use 1901 for the example) and the pull request branch that we will use is feature-fix-numpy,
then you will use a variation of the following git command: git fetch origin pull/<#>/head:<local_branch_name>,
e.g.:
Note: Notice that we fetch from upstream, since that is the original project, where the pull request is supposed to be
Tip: The amount of work of some users maybe worth it to add his remote to your fork’s git configuration, to do so
with the imaginary github user Obi-Wan Kenobi which nickname is obiwankenobi, you will do:
And to fetch the pull request branch that we put as example, you would do:
Sometimes you may prefer to use directly the fork of the user, so you will get the nickname of the user who created
the pull request, let’s take the same imaginary user than before obiwankenobi:
Note: You can view the author/branch information looking at the subtitle of the pull request, near the pull request
status (expected an open status)
1.13.2 Using python-for-android commands directly from the pull request files
• Enter inside the directory of the cloned repository in the above step and run p4a command with proper args, e.g.
(to test an modified pycryptodome recipe)
cd p4a-feature-fix-numpy
PYTHONPATH=. python3 -m pythonforandroid.toolchain apk \
--private=testapps/on_device_unit_tests/test_app \
(continues on next page)
42 Chapter 1. Contents
python-for-android Documentation, Release 0.1
1.13.3 Installing python-for-android using the github’s branch of the pull request
• Enter inside the directory of the cloned repository mentioned in Common steps and install it via pip, e.g.:
cd p4a-feature-fix-numpy
pip3 install . --upgrade --user
• Now, go inside the testapps/on_device_unit_tests directory (we assume that you still are inside the cloned repos-
itory)
cd testapps/on_device_unit_tests
• Run the build of the apk via the freshly installed copy of python-for-android by running a similar command than
below
Tip: if you don’t want to mess up with the system’s python, you could do the same steps but inside a virtualenv
Warning: Once you finish the pull request tests remember to go back to the master or develop versions of
python-for-android, since you just installed the python-for-android files of the pull request
• Edit your buildozer.spec file. You should search for the key p4a.source_dir and set the right value so in the
example posted in Common steps it would look like this:
p4a.source_dir = /home/user/p4a_pull_requests/p4a-feature-fix-numpy
Note: this method has the advantage, can be run without installing the pull request version of python-for-android
nor the android’s dependencies but has one problem. . . when things goes wrong you must determine if it’s a buildozer
issue or a python-for-android one
Warning: Once you finish the pull request tests remember to comment/edit the p4a.source_dir constant that you
just edited to test the pull request
Tip: this method it’s useful for developing pull requests since you can edit p4a.source_dir to point to your python-
for-android fork and you can test any branch you want only switching branches with: git checkout <branch-name>
from inside your python-for-android fork
44 Chapter 1. Contents
CHAPTER 2
• genindex
• modindex
• search
45
python-for-android Documentation, Release 0.1
a
android.activity, 17
android.broadcast, 17
android.runnable, 18
47
python-for-android Documentation, Release 0.1
Symbols
__init__() (android.broadcast.BroadcastReceiver
method), 18
A
android.activity (module), 16, 17
android.broadcast (module), 17
android.runnable (module), 18
B
bind() (in module android.activity), 16
BroadcastReceiver (class in android.broadcast),
17
R
register_activity_lifecycle_callbacks()
(in module android.activity), 17
S
start() (android.broadcast.BroadcastReceiver
method), 18
stop() (android.broadcast.BroadcastReceiver
method), 18
U
unbind() (in module android.activity), 16
unregister_activity_lifecycle_callbacks()
(in module android.activity), 17
49