Skip to content

Commit

Permalink
Merge pull request #33 from joaoventura/p4a
Browse files Browse the repository at this point in the history
Python-for-Android
  • Loading branch information
joaoventura committed May 14, 2020
2 parents b7640d8 + 7f9c863 commit 5e152ea
Show file tree
Hide file tree
Showing 63 changed files with 41 additions and 24 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified PyApp/app/src/main/assets/python/stdlib.zip
Binary file not shown.
4 changes: 2 additions & 2 deletions PyApp/app/src/main/java/com/jventura/pyapp/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ protected void onCreate(Bundle savedInstanceState) {
try {
JSONObject json = new JSONObject();
json.put("function", "greet");
json.put("name", "Python 3.5");
json.put("name", "Python 3.8");

JSONObject result = PyBridge.call(json);
String answer = result.getString("result");

TextView textView = (TextView) findViewById(R.id.textView);
TextView textView = findViewById(R.id.textView);
textView.setText(answer);

} catch (JSONException e) {
Expand Down
22 changes: 16 additions & 6 deletions PyApp/app/src/main/jni/Android.mk
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
LOCAL_PATH := $(call my-dir)
CRYSTAX_PATH := /Users/jventura/Library/Android/crystax-ndk-10.3.2

# Python-for-Android paths

PYTHON_FOR_ANDROID_PATH := /Users/jventura/Desktop/Shared/python-for-android
GUEST_PYTHON_PATH := $(PYTHON_FOR_ANDROID_PATH)/build/other_builds/python3
PYTHON_PATH := $(GUEST_PYTHON_PATH)/armeabi-v7a__ndk_target_21/python3


# Build libpybridge.so
Expand All @@ -8,14 +13,19 @@ include $(CLEAR_VARS)
LOCAL_MODULE := pybridge
LOCAL_SRC_FILES := pybridge.c
LOCAL_LDLIBS := -llog
LOCAL_SHARED_LIBRARIES := python3.5m
LOCAL_SHARED_LIBRARIES := python3.8m
include $(BUILD_SHARED_LIBRARY)


# Include libpython3.5m.so
# Include libpython3.8m.so

include $(CLEAR_VARS)
LOCAL_MODULE := python3.5m
LOCAL_SRC_FILES := $(CRYSTAX_PATH)/sources/python/3.5/libs/$(TARGET_ARCH_ABI)/libpython3.5m.so
LOCAL_EXPORT_CFLAGS := -I $(CRYSTAX_PATH)/sources/python/3.5/include/python/
LOCAL_MODULE := python3.8m
LOCAL_SRC_FILES := $(PYTHON_PATH)/android-build/libpython3.8m.so
LOCAL_EXPORT_CFLAGS := -I $(PYTHON_PATH)/Include
include $(PREBUILT_SHARED_LIBRARY)


# The assets path
# ASSETS_PATH := $(PYTHON_FOR_ANDROID_PATH)/dists/unnamed_dist_1__armeabi-v7a/_python_bundle/_python_bundle
# NOTE: Copy assets from the p4a dist path to ../assets/python along with bootstrap.py
6 changes: 3 additions & 3 deletions PyApp/app/src/main/jni/pybridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,15 @@ JNIEXPORT jint JNICALL Java_com_jventura_pybridge_PyBridge_start

// Build paths for the Python interpreter
char paths[512];
snprintf(paths, sizeof(paths), "%s:%s/stdlib.zip", pypath, pypath);
snprintf(paths, sizeof(paths), "%s:%s/stdlib.zip:%s/modules", pypath, pypath, pypath);

// Set Python paths
wchar_t *wchar_paths = Py_DecodeLocale(paths, NULL);
Py_SetPath(wchar_paths);

// Initialize Python interpreter and logging
PyImport_AppendInittab("androidlog", PyInit_androidlog);
Py_Initialize();
Py_InitializeEx(0);
setAndroidLog();

// Bootstrap
Expand Down Expand Up @@ -154,7 +154,7 @@ JNIEXPORT jstring JNICALL Java_com_jventura_pybridge_PyBridge_call

// Call function and get the resulting string
PyObject* myResult = PyObject_CallObject(myFunction, args);
char *myResultChar = PyUnicode_AsUTF8(myResult);
const char *myResultChar = PyUnicode_AsUTF8(myResult);

// Store the result on a java.lang.String object
jstring result = (*env)->NewStringUTF(env, myResultChar);
Expand Down
33 changes: 20 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ PyBridge is a JNI implementation that allows you to reuse your existing Python c
Android Java application. It allows you to send String or JSON messages to your Python interpreter
without the need for network frameworks. Instead of using web applications disguised as native
applications, you can reuse your Python backend code and implement truly native Android applications.
PyBridge uses the Python 3.5 distribution bundled with [Crystax NDK](https://www.crystax.net/).

PyBridge is being used in production on [one of my Android apps](https://play.google.com/store/apps/details?id=com.flatangle.charts)
and it shares a large amount of code with [one of my web applications](http://elements.flatangle.com/).
PyBridge is being used in production on [one of my Android apps](https://play.google.com/store/apps/details?id=com.flatangle.charts) and it shares a large amount of code with [one of my web applications](http://elements.flatangle.com/).

*Shameless plug:* I do contract work, check out my website at http://joaoventura.net/ or buy my apps!

Expand All @@ -25,13 +23,25 @@ initializes the Python interpreter and sets a message on the TextView.

Clone this project and open it on the latest Android Studio.

To build the pybridge shared library you will need to download the Crystax NDK from
https://www.crystax.net/en/download. Open the `app/src/main/jni/Android.mk` file and change the
`CRYSTAX_PATH` to match the path of your Crystax NDK installation. Finally, open the terminal,
cd to `app/src/main/jni`, and run `path/to/crystax/ndk-build`. You should have libcrystax,
libpython3.5 and libpybridge in src/main/libs.
To build the pybridge shared library you will need first to compile a guest libpython3.8m.so for the architecure and then use the compiled sources to build the pybridge shared library.

Run the project in the Android Studio and you should see a `Hello Python 3.5` message in the screen.
### Compile Python 3.8 for Android

PyBridge uses the [python-for-android](https://python-for-android.readthedocs.io/en/latest/) project to compile the necessary shared libraries (libpython3.8m.so) and standard lib modules.

I use `p4a create --requirements=python3 --blacklist-requirements=sqlite3,android,libffi,openssl` to start the build process.

The generated files are usually inside `~/.local/share/python-for-android/` on Linux. The shared library is in `build/other_builds/python3/.../android-build/` and the compiled stdlib is in `dists/.../_python_bundle`. These files are necessary to compile libpybridge.so and run the project.

### Compile libpybridge.so

Open the `app/src/main/jni/Android.mk` file and change the `PYTHON_FOR_ANDROID_PATH` (and maybe the others) to match the path of the python-for-android generated files. Finally, open the terminal, cd to `app/src/main/jni`, and run `path/to/ndk/ndk-build`. You should have libpython3.8 and libpybridge in src/main/libs.

### Copy the standard library

You must copy the stdlib files in `_python_bundle` to `assets/python` along with bootstrap.py. Those files may already be there, but update them if you use another python version.

Finally, run the project in the Android Studio and you should see a `Hello Python 3.8` message in the screen.


## How it works
Expand Down Expand Up @@ -68,10 +78,7 @@ only when it runs on the first time or after the application updates.

## Limitations

PyBridge uses the Python 3.5 distribution bundled with [Crystax NDK](https://www.crystax.net/).
The Crystax NDK allows you, in theory, to use or compile any C python module out there.
Bundle the compiled modules in the python assets folder together with the standard library, import
them and you're done.
PyBridge uses python-for-android to cross-compile Python 3.8 to Android. In theory you can use other python-for-android recipes to cross-compile other python libraries such as Numpy. Depending on the library, bundle the compiled modules in the python assets folder together with the standard library, import them and you're probably done.

The performance of the Python interpreter on modern smartphones is more than enough for most use cases,
but you should always consider wrapping PyBridge calls in a separate thread so that you do not block
Expand Down

0 comments on commit 5e152ea

Please sign in to comment.