Android¶
Chaquopy is distributed as a plugin for Android’s Gradle-based build system.
Prerequisites:
Android Gradle plugin version should be between 3.3 and 4.0. This is specified as
com.android.tools.build:gradle
in your project’s top-levelbuild.gradle
file, and will usually be the same as your Android Studio version.Older versions as far back as 2.2 are supported by older versions of Chaquopy: see this page. Newer versions may also work, but have not been tested with this version of Chaquopy.
- minSdkVersion must be at least 16. Older versions as far back as 15 are supported by older versions of Chaquopy: see this page.
Basic setup¶
Gradle plugin¶
In the project’s top-level build.gradle
file, add the Chaquopy Maven repository and
dependency to the end of the existing repositories
and dependencies
blocks:
buildscript { repositories { ... maven { url "https://chaquo.com/maven" } } dependencies { ... classpath "com.chaquo.python:gradle:8.0.1" } }
Then, in the module-level build.gradle
file (usually in the app
directory), apply the
Chaquopy plugin at the top of the file, but after the Android plugin:
apply plugin: 'com.android.application'
apply plugin: 'com.chaquo.python' // Add this line
All other configuration will be done in this module-level build.gradle
. The examples below
will show the configuration within defaultConfig
, but it can also be done within a product
flavor.
The Chaquopy plugin can also be used in an Android library module (AAR). However, it can only be used in one module in a project: either in the app module, or in exactly one library module. Attempting to use it in multiple modules will give the error “More than one file was found with OS independent path”.
ABI selection¶
The Python interpreter is a native component, so you must use the abiFilters setting to specify which ABIs you want the app to support. The currently available ABIs are:
armeabi-v7a
, supported by virtually all Android devices.arm64-v8a
, supported by most recent Android devices.x86
, for the Android emulator.x86_64
, for the Android emulator.
During development you will probably want to enable ABIs for both the emulator and your devices, e.g.:
defaultConfig {
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
If you get the error “No version of NDK matched the requested version”, you can fix this as follows:
- Go to Tools > SDK Manager.
- Select the “SDK Tools” tab.
- Select “Show Package Details” at the bottom.
- Find “NDK (Side by side)” in the list, and select the version mentioned in the message.
- Click OK.
You may also see the warning “Compatible side by side NDK version was not found”. This is harmless, and there’s no need to actually install the NDK, as all of Chaquopy’s native libraries are already pre-compiled and stripped. However, you can silence the warning using the same steps as above.
Note
Each ABI will add several MB to the size of the app, plus the size of any native requirements. Because of the way the native components are packaged, the split APK and app bundle features will not fully mitigate this. Instead, if your multi-ABI APKs are too large, try using a product flavor dimension:
android {
flavorDimensions "abi"
productFlavors {
arm {
dimension "abi"
ndk { abiFilters "armeabi-v7a" }
}
x86 {
dimension "abi"
ndk { abiFilters "x86" }
}
}
}
Development¶
Some features require Python 3.5 or later to be available on the build machine. These features are indicated by a note in their documentation sections.
By default, Chaquopy will try to run Python with the standard command for your operating
system, first with a matching minor version, and then with a matching major version. For
example, if Chaquopy’s own Python version is 3.8.x, then on Linux and Mac
it will first try python3.8
, then python3
. On Windows, it will first try py -3.8
, then
py -3
.
If this doesn’t work for you, set your Python command using the buildPython
setting.
For example, on Windows you might use the following:
defaultConfig {
python {
buildPython "C:/path/to/python.exe"
}
}
Source code¶
By default, Chaquopy will look for Python source code in the python
subdirectory of each
source set. For example,
the Python code for the main
source set should go in src/main/python
.
To include Python source code from other directories, use the android.sourceSets block. For example:
android {
sourceSets {
main {
python.srcDir "some/other/dir"
}
}
}
Note
The setRoot method only takes effect on the standard Android directories. If you want to set the Python directory as well, you must do so explicitly, e.g.:
main {
setRoot "some/other/main"
python.srcDirs = ["some/other/main/python"]
}
As with Java, it is
usually an error if the source directories for a given build variant include multiple copies of
the same filename. This is only permitted if the duplicate files are all empty, such as may
happen with __init__.py
.
Startup¶
It’s important to structure the app so that Python.start() is always called with an AndroidPlatform before attempting to run Python code. There are two basic ways to achieve this:
If the app always uses Python, then call Python.start() from a location which is guaranteed to run exactly once per process, such as Application.onCreate(). A PyApplication subclass is provided to make this easy: simply add the following attribute to the
<application>
element inAndroidManifest.xml
:android:name="com.chaquo.python.android.PyApplication"
You can also use your own subclass of
PyApplication
here.Alternatively, if the app only sometimes uses Python, then call Python.start() after first checking whether it’s already been started:
// "context" must be an Activity, Service or Application object from your app. if (! Python.isStarted()) { Python.start(new AndroidPlatform(context)); }
Requirements¶
Note
This feature requires Python on the build machine, which can be configured with the buildPython setting.
External Python packages may be built into the app using the pip
block in build.gradle
.
Within this block, add install
lines, each specifying a package in one of the following
forms:
defaultConfig {
python {
pip {
// A pip requirement specifier, with or without a version number:
install "scipy"
install "requests==2.24.0"
// An sdist or wheel filename, relative to the project directory:
install "MyPackage-1.2.3-py2.py3-none-any.whl"
// "-r"` followed by a requirements filename, relative to the
// project directory:
install "-r", "requirements.txt"
}
}
}
In our most recent tests, Chaquopy could install over 90% of the top 1000 packages on PyPI. This includes almost all pure-Python packages, plus a constantly-growing selection of packages with native components. To see which native packages and versions are currently available, you can browse the repository here. To request a package to be added or updated, or for any other problem with installing requirements, please visit our issue tracker.
To pass options to pip install
, give them as a comma-separated list to the options
setting.
For example:
pip {
options "--extra-index-url", "https://example.com/private/repository"
install "MyPackage==1.2.3"
}
Any options in the pip documentation may be used, except for those
which relate to the target environment, such as --target
, --user
or -e
. If there are
multiple options
lines, they will be combined in the order given.
Static proxy generator¶
Note
This feature requires Python on the build machine, which can be configured with the buildPython setting.
In order for a Python class to extend a Java class, or to be referenced by name in Java code or
in AndroidManifest.xml
, a Java proxy class must be generated for it. The staticProxy
setting specifies which Python modules to search for these classes:
defaultConfig {
python {
staticProxy "module.one", "module.two"
}
}
The app’s source code and requirements
will be searched, in that order, for the specified modules. Either simple modules (e.g.
module/one.py
) or packages (e.g. module/one/__init__.py
) may be used.
Within the modules, static proxy classes must be declared using the syntax described in the static proxy section. For all declarations found, Java proxy classes will be generated and built into the app.
Packaging¶
Data files¶
To save time and space, your app’s Python modules are loaded directly from the APK at runtime
and don’t exist as separate files on the device. However, each module’s __file__
and
__path__
attributes can still be used in the normal way to find any data files which you
included in your source directory.
To speed up app startup, data files within a top-level package (i.e. a directory containing an
__init__.py
file) won’t be extracted from the APK until the first time that package is
imported. All other data files will be extracted the first time the app is started.
Bytecode compilation¶
Note
This feature requires Python on the build machine, which can be configured with the buildPython setting.
Your app will start up faster if its Python code is compiled to .pyc
format, so this is
enabled by default.
Compilation prevents source code text from appearing in stack traces, so during development you may wish to disable it. There are individual settings for:
src
: local source codepip
: requirementsstdlib
: the Python standard library
For example, to disable compilation of your local source code:
defaultConfig {
python {
pyc {
src false
}
}
}
In the case of src
and pip
, your buildPython must use the same
bytecode format as Chaquopy itself. Usually this means it must have the same minor version,
e,g. if Chaquopy’s own Python version is 3.8.x, then buildPython
can be
any version of Python 3.8.
If the bytecode formats do not match, the build will continue with a warning, unless you’ve
explicitly set one of the pyc
settings to true
. Your app will still work, but its code will
have to be compiled on the target device, which means it will start up slower and use more
storage space.
Python standard library¶
Unsupported modules¶
All standard library modules are supported, except for the following:
ssl¶
For consistency across different devices, the ssl
module is configured to use a copy of
the CA bundle from certifi. The current version
is from certifi 2020.4.5.1.
sys¶
stdout
and stderr
are redirected to Logcat with the tags python.stdout
and
python.stderr
respectively. The streams will produce one log line for each call to write()
,
which may result in lines being split up in the log. Lines may also be split if they exceed the
Logcat message length limit of approximately 4000 bytes.
stdin
always returns EOF. If you want to run some code which takes interactive text input, you
may find the console app template useful.
Android Studio plugin¶
To add Python editing suppport to the Android Studio user interface, you may optionally install the “Python Community Edition” plugin. However, Chaquopy isn’t integrated with this plugin, so you’ll see the warning “No Python interpreter configured for the module”, and your code will probably display many error indicators. These are harmless: just go ahead and run your app, and if there really is an error, the details will be displayed in the Logcat.