PyJNIus Documentation

repository·master·Indexed 23 days ago

https://github.com/kivy/pyjnius

A Python library that enables access to Java classes via the Java Native Interface (JNI). It is widely used in the Kivy ecosystem to access Android APIs through python-for-android. Key features include the autoclass() function for automatic class discovery, manual Java class definition via MetaJavaClass, and support for Android-specific hardware and system APIs such as MediaRecorder, MediaPlayer, and TextToSpeech.

Tokens
8K
Snippets
27
Records
39
Agent score
81%

What's inside PyJNIus

  1. Overview of PyJNIus

    master

    PyJNIus is a Python library that enables access to Java classes via the Java Native Interface (JNI). It functions in two modes:

    1. Starting a new JVM: It can launch a new Java Virtual Machine inside the current process.
    2. Retrieving an existing JVM: It can attach to an already running JVM, which is the standard behavior on the Android platform.

    It is managed by the Kivy Team and is compatible with python-for-android.

  2. Reflect a Java class using JavaClass

    master

    To access a Java class from Python, subclass JavaClass and define the following required attributes:

    1. __javaclass__: The Java class name in 'path/to/Class' format (e.g., 'java/util/Stack').
    2. __metaclass__: Must be set to MetaJavaClass.
      • In Python 2: __metaclass__ = MetaJavaClass
      • In Python 3: class Stack(JavaClass, metaclass=MetaJavaClass):

    Optional attributes:

    • __javaconstructor__: A tuple of all possible constructor signatures in JNI format (e.g., ('()V', '(Ljava/lang/String;)V')). If not set, a no-argument constructor is assumed.

    You can then add JavaMethod, JavaStaticMethod, JavaField, or JavaStaticField to map specific members.

    from jnius import JavaClass, MetaJavaClass
    
    class Stack(JavaClass):
        __javaclass__ = 'java/util/Stack'
        __metaclass__ = MetaJavaClass
  3. How automatic recursive inspection works

    master

    PyJNIus utilizes Java reflection to automatically wrap returned Java objects. If a method or field returns a Java object that is not a native type, PyJNIus provides a new autoclass-like object that allows you to continue accessing the returned object's members naturally without manual re-declaration.

    For example, accessing java.lang.System.out automatically provides access to the PrintStream object's methods.

    from jnius import autoclass
    
    System = autoclass('java.lang.System')
    # System.out is automatically reflected as a Java object
    System.out.println('Hello World')
  4. Understand Java signature format

    master

    When implementing Java in Python, the signature of the Java method must match exactly. Java signatures follow the format (<argument1><argument2><...>)<return type>.

    Type Identifiers:

    • L<java class>;: A Java object of type <java class>
    • Z: java/lang/Boolean
    • B: java/lang/Byte
    • C: java/lang/Character
    • S: java/lang/Short
    • I: java/lang/Integer
    • J: java/lang/Long
    • F: java/lang/Float
    • D: java/lang/Double
    • V: void (return type only)
    • [ prefix: Indicates an array (e.g., [B is a Byte[])

    Examples:

    • (ILjava/util/List;)V: Argument 1 is an Integer, Argument 2 is a java.util.List, returns void.
    • ([B)Z: Argument 1 is a Byte[], returns a boolean.

    You can use the javap tool to find these signatures. For Android classes, use the -classpath flag pointing to android.jar within your Android SDK platforms directory.

  5. Implement Java interfaces in Python using PythonJavaClass

    master

    To implement a Java interface in Python so it can be passed to Java, subclass PythonJavaClass.

    Requirements:

    1. Define __javainterfaces__: A list of Java interfaces in 'path/to/Interface' format.
    2. Use the @java_method(signature) decorator on Python methods to map them to the interface.
    3. Limitations: You can only implement interfaces (you cannot subclass Java objects) and you cannot implement static methods/fields. You must keep a reference to the Python object alive as long as Java is using it.

    Class Loaders (__javacontext__):

    • 'system' (default): Used for standard Java API interfaces.
    • 'app': Required on Android if you are implementing an interface that is part of your own APK/application code.
    from jnius import PythonJavaClass, java_method
    
    class PythonListIterator(PythonJavaClass):
        __javainterfaces__ = ['java/util/ListIterator']
    
        def __init__(self, collection, index=0):
            super(PythonListIterator, self).__init__()
            self.collection = collection
            self.index = index
    
        @java_method('()Z')
        def hasNext(self):
            return self.index < len(self.collection.data) - 1
    
        @java_method('()Ljava/lang/Object;')
        def next(self):
            obj = self.collection.data[self.index]
            self.index += 1
            return obj
  6. Understand the PyJNIus Android wheel runtime contract

    master

    PyJNIus can be distributed as a prebuilt Android wheel (android_* tags) that is SDL-agnostic and Java-free. Unlike the desktop version, the Android wheel does not bundle a JVM or Java classes; instead, it attaches to the JVM already running in the host process.

    To use this wheel, the host application must satisfy two requirements:

    1. In-process JVM discovery: The host must have a JVM discoverable at the time of import jnius. PyJNIus attempts to resolve JNIEnv in this order:

      • SDL_GetAndroidJNIEnv from libSDL3.so (SDL3)
      • SDL_AndroidGetJNIEnv from libSDL2.so (SDL2)
      • JNI_GetCreatedJavaVMs from libnativehelper.so (API 31+ only)

      Note: For SDL hosts, the SDL library must be loaded before the first import jnius occurs.

    2. Java glue on the dex classpath: If using features like PythonJavaClass or @java_method (proxies), the host application's packager must compile and include org.jnius.NativeInvocationHandler in the APK's dex classpath. Plain autoclass and method calls do not require this glue.

  7. Access nested Java classes using the $ syntax

    master
    To access nested classes in Java via PyJNIus, use the $ character in the class name string passed to autoclass. For example, to access android.provider.MediaStore.Images.Media, use autoclass('android.provider.MediaStore$Images$Media').
  8. Manually define Java classes with MetaJavaClass

    master

    For more control or to explicitly declare only the methods and fields you need, you can manually define a Python class that inherits from JavaClass and uses MetaJavaClass as its metaclass. You must provide the __javaclass__ (the Java class path using slashes instead of dots) and define methods using JavaStaticMethod or JavaMethod with their JNI signatures.

    from jnius import MetaJavaClass, JavaClass, JavaMethod, JavaStaticMethod
    
    class Hardware(JavaClass):
        __metaclass__ = MetaJavaClass
        __javaclass__ = 'org/renpy/android/Hardware'
        vibrate = JavaStaticMethod('(D)V')
        accelerometerEnable = JavaStaticMethod('(Z)V')
        accelerometerReading = JavaStaticMethod('()[F')
        getDPI = JavaStaticMethod('()I')
    
    # Usage
    print('DPI is', Hardware.getDPI())
    Hardware.accelerometerEnable(True)
  9. Build PyJNIus from source

    master

    Building PyJNIus is required for development or when a pre-built binary is unavailable for your platform.

    Prerequisites

    All platforms require a Java Development Kit (JDK). If PyJNIus cannot locate your JDK, set the JAVA_HOME environment variable.

    Platform-Specific Build Requirements

    • Linux: GNU Compiler Collection (GCC). On Debian-based systems, use apt-get install build-essentials.
    • Windows: Microsoft Visual C++ Build Tools (the command-line tools subset of Visual Studio).
    • macOS: Xcode command-line tools.

    Installation

    After checking out the repository, install it using pip:

    pip install .

    This will install Cython in the Python build environment as specified in pyproject.toml.

  10. Build PyJNIus Android wheels

    master

    To build redistributable Android wheels, use a Linux x86_64 or macOS host with an Android SDK. The process involves generating a Cython-produced C file and using cibuildwheel to drive the NDK.

    Important Build Requirements:

    • Clean Tree: Always build from a clean tree (e.g., rm -rf build) to prevent stale Java payloads from being re-injected, which violates the Java-free guarantee of the wheel.
    • Page Alignment: The build is configured to use LDFLAGS = "-Wl,-z,max-page-size=16384" to ensure 16 KB page alignment required by Android 15/16.
    • Frontend: Use build or uv as the frontend; Android does not support pip for this process.