There's a very popular question on SO "How to run python on android" but nobody actually explains what you have to do or how it's done.
Since I went through all of these projects for hours to figure it out I thought I'd share how you actually do it.
From a high level, to run just python on android you must:
So once python is compiled for the correct arch the app must simply know where to find it and it can then access the python C-API, which can then "run" any python code.
This is how the python-for-android (ex kivy), pybridge, and pybee's implementations all work.
If you need extensions or other libraries that require rebuilding python (ex SSL), just use python-for-android. Cross compiling is REALLY complicated, every library is different. Python-for-android has a lot of the popular ones already done. To add your own extension you create a new recipe and implement it (just read how the other recipes work). It's mainly a matter of finding the correct -I and -L and -l flags the library needs (and the paths to them during building).
Since I went through all of these projects for hours to figure it out I thought I'd share how you actually do it.
From a high level, to run just python on android you must:
- Cross compile python and any extensions for the target ABIs (arm, arm-v7a, etc.. see https://developer.android.com/ndk/guides/abis.html)
- Include the cross compiled libpython and all CC'd extensions as native libraries (gradle's jniLibs https://developer.android.com/studio/projects/add-native-code.html#link-gradle) or optionally extract them to the assets folder
- Include the Python standard library and any extra python files as android assets (compress in a zip and have the app extract to assets on first launch)
- Create a C or C++ native file using the NDK and have it initialize Python using the C-API (https://docs.python.org/2/c-api/intro.html#embedding-python) as an embeded interpreter
- Use the JNI to invoke your native code, creating any API's needed (ex run a main.py script or invoke a python function).
So once python is compiled for the correct arch the app must simply know where to find it and it can then access the python C-API, which can then "run" any python code.
This is how the python-for-android (ex kivy), pybridge, and pybee's implementations all work.
Example
The most simple example of this is in pybridge. It looks like it's a simplified version of Kivy's.Setup
You have to have the Android SDK and NDK (Crystax) installed.Cross compiling
pybridge uses the prebuilt python (2.7 or 3.5) from the Crystax NDK. Which is the easiest because it has libpython2.7.so (or 3.5m) already compiled for each arch along with several modules from the standard library (ex sockets, multiprocessing) and it has the python stdlib packaged as a zip for you.If you need extensions or other libraries that require rebuilding python (ex SSL), just use python-for-android. Cross compiling is REALLY complicated, every library is different. Python-for-android has a lot of the popular ones already done. To add your own extension you create a new recipe and implement it (just read how the other recipes work). It's mainly a matter of finding the correct -I and -L and -l flags the library needs (and the paths to them during building).
Including libpython
Kivy and pybridge use an Android.mk file to include python and ndk-build to complile the .c file that provides the needed API. Pybridge then uses Gradle to copy ".so" files within the jnilibs source directory into the apk for you. Android installs any ".so" file in the jniLibs.srcDir that starts with the "lib" prefix, ex libpython.so so these don't need extracted from the apk.Including the standard library (and other python packages)
Pybridge just uses the stdlib.zip packed with the crystax NDK. It adds this zip into a folder within the android assets directory (assets/python/stdlib.zip). When the app starts, the main activty uses an "AssetExtractor" which extracts all the files from the apk (apk is just a zip file) to the actual assets folder on the device. After this is done, python simply needs to know the path and it can use them.
You can include any python source packages this way. It's added to the system path and it just works normally.
Create a native bootstrap
The bootstrap is just a native c file that embeds the python interpreter and does any initialization (such as redirecting stdout to use android's log). Pybridge and Kivy's are nearly identical. https://github.com/joaoventura/pybridge/blob/master/app/src/main/jni/pybridge.c If you look at this file it is using the Python C-API to initialize the python interpreter, set the python path (to the android assets folder) and then it imports the bootstrap file. Kivy simply runs a "main.py" file at this point and handles the rest in python (well mostly).
Call the native bootstrap via the JNI
Kivy and Pybridge's main activities then simply define a JNI wrapper to the bootstrap functions (ex start) and invoke these after extracting the assets. Pybridge uses JSON and the JNI as a sort of RPC to invoke python from Java and return the result. Kivy simply kicks off the main.py script.
That's all. Read the code it's not too bad (except compiling that is)!
Cheers!
Hey, how can i include numpy library in this project ?
ReplyDelete