Permalink
386 lines (271 sloc) 13 KB

API

This part of the documentation covers all the interfaces of Pyjnius.

Reflection classes

Base for reflecting a Java class. The idea is to subclass this JavaClass, add few :class:`JavaMethod`, :class:`JavaStaticMethod`, :class:`JavaField`, :class:`JavaStaticField`, and you're done.

You need to define at minimum the :data:`__javaclass__` attribute, and set the :data:`__metaclass__` to :class:`MetaJavaClass`.

So the minimum class definition would look like:

from jnius import JavaClass, MetaJavaClass

class Stack(JavaClass):
    __javaclass__ = 'java/util/Stack'
    __metaclass__ = MetaJavaClass

Reflection of a Java method.

Reflection of a static Java method.

Reflection of a Java field.

Reflection of a static Java field

Reflection of a Java method that can be called from multiple signatures. For example, the method getBytes in the String class can be called from:

public byte[] getBytes(java.lang.String)
public byte[] getBytes(java.nio.charset.Charset)
public byte[] getBytes()

Let's see how you could declare that method:

class String(JavaClass):
    __javaclass__ = 'java/lang/String'
    __metaclass__ = MetaJavaClass

    getBytes = JavaMultipleMethod([
        '(Ljava/lang/String;)[B',
        '(Ljava/nio/charset/Charset;)[B',
        '()[B'])

Then, when you will try to access to this method, we'll take the best method available according to the type of the arguments you're using. Internally, we are calculating a "match" score for each available signature, and take the best one. Without going into the details, the score calculation look like:

  • a direct type match is +10
  • a indirect type match (like using a float for an int argument) is +5
  • object with unknown type (:class:`JavaObject`) is +1
  • otherwise, it's considered as an error case, and return -1

Reflection functions

Java class implementation in Python

Base for creating a Java class from a Python class. This allow to implement java interface completely in Python.

In reality, you'll create a Python class that mimic the list of declared :data:`__javainterfaces__`. When you'll give an instance of this class to Java, Java will just accept it and call the interfaces methods as declared. Under the hood, we are catching the call, and redirecting to use your declared Python method.

Your class will act as a Proxy to the Java interfaces.

You need to define at minimum the :data:`__javainterfaces__` attribute, and declare java methods with the :func:`java_method` decorator.

Note

Static methods and static fields are not supported

For example, you could implement the java/util/ListIterator interface in Python like that:

from jnius import PythonJavaClass, java_method

class PythonListIterator(PythonJavaClass):
    __javainterfaces__ = ['java/util/ListIterator']

    def __init__(self, collection, index=0):
        super(TestImplemIterator, 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

    # etc...

Java signature format

Java signatures have a special format that could be difficult to understand at first. Let's see in details. A signature is in the format:

(<argument1><argument2><...>)<return type>

All the types for any part of the signature can be one of:

  • L<java class>; = represent a Java object of the type <java class>
  • Z = represent a java/lang/Boolean;
  • B = represent a java/lang/Byte;
  • C = represent a java/lang/Character;
  • S = represent a java/lang/Short;
  • I = represent a java/lang/Integer;
  • J = represent a java/lang/Long;
  • F = represent a java/lang/Float;
  • D = represent a java/lang/Double;
  • V = represent void, available only for the return type

All the types can have the [ prefix to design an array. The return type can be V or empty.

A signature like:

(ILjava/util/List;)V
-> argument 1 is an integer
-> argument 2 is a java.util.List object
-> the method doesn't return anything.

(java.util.Collection;[java.lang.Object;)V
-> argument 1 is a Collection
-> argument 2 is an array of Object
-> nothing is returned

([B)Z
-> argument 1 is a Byte []
-> a boolean is returned

When you implement Java in Python, the signature of the Java method must match. Java provides a tool named javap to get the signature of any java class. For example:

$ javap -s java.util.Iterator
Compiled from "Iterator.java"
public interface java.util.Iterator{
public abstract boolean hasNext();
  Signature: ()Z
public abstract java.lang.Object next();
  Signature: ()Ljava/lang/Object;
public abstract void remove();
  Signature: ()V
}

JVM options and the class path

JVM options need to be set before import jnius is called, as they cannot be changed after the VM starts up. To this end, you can:

import jnius_config
jnius_config.add_options('-Xrs', '-Xmx4096')
jnius_config.set_classpath('.', '/usr/local/fem/plugins/*')
import jnius

If a classpath is set with these functions, it overrides any CLASSPATH environment variable. Multiple options or path entries should be supplied as multiple arguments to the add_ and set_ functions. If no classpath is provided and CLASSPATH is not set, the path defaults to '.'. This functionality is not available on Android.

Pyjnius and threads

Example:

import threading
import jnius

def run(...):
    try:
        # use pyjnius here
    finally:
        jnius.detach()

If you don't, it will crash on dalvik and ART / Android:

D/dalvikvm(16696): threadid=12: thread exiting, not yet detached (count=0)
D/dalvikvm(16696): threadid=12: thread exiting, not yet detached (count=1)
E/dalvikvm(16696): threadid=12: native thread exited without detaching
E/dalvikvm(16696): VM aborting

Or:

W/art     (21168): Native thread exiting without having called DetachCurrentThread (maybe it's going to use a pthread_key_create destructor?): Thread[16,tid=21293,Native,Thread*=0x4c25c040,peer=0x677eaa70,"Thread-16219"]
F/art     (21168): art/runtime/thread.cc:903] Native thread exited without calling DetachCurrentThread: Thread[16,tid=21293,Native,Thread*=0x4c25c040,peer=0x677eaa70,"Thread-16219"]
F/art     (21168): art/runtime/runtime.cc:203] Runtime aborting...
F/art     (21168): art/runtime/runtime.cc:203] (Aborting thread was not attached to runtime!)
F/art     (21168): art/runtime/runtime.cc:203] Dumping all threads without appropriate locks held: thread list lock mutator lock
F/art     (21168): art/runtime/runtime.cc:203] All threads:
F/art     (21168): art/runtime/runtime.cc:203] DALVIK THREADS (16):
...