pyjnius库:一个用于访问Java类的Python库
参考链接:
https://pyjnius.readthedocs.io/en/stable/installation.html#installation-for-windows
1 Windows的安装
Python 和 pip 必须安装并显示在 PATH 环境变量中。
下载并安装包含 JRE 的 JDK:
Http://www.oracle.com/technetwork/java/javase/downloads/index.html
编辑系统的环境变量(在路径中使用适当的 Java 位和版本) :
JAVA_HOME
: C:\Program Files\Java\jdk1.7.0_79\binPATH
: C:\Program Files\Java\jdk1.7.0_79\jre\bin\server 包含导入和使用 PyJnius 所必需的 jvm.dll。
把JAVA_HOME
也添加到系统变量中,%JAVA_HOME%
。
下载并安装C语言编译器:
Microsoft Visual C++ Compiler for Python 2.7:http://aka.ms/vcpython27
MinGWPy for Python 2.7:https://anaconda.org/carlkl/mingwpy
Microsoft Visual C++ Build Tools (command-line tools subset of Visual Studio) for Python 3.5 and 3.6:https://visualstudio.microsoft.com/downloads/
其他版本Python需要参照链接 Windows Compilers wiki.
更新pip和setuptools:
1 |
|
安装Cython:
1 |
|
安装Pyjnius:
1 |
|
2 快速开始
一个简单的 Pyjnius 示例如下:
1 |
|
只需将它保存为 test.py (或类似的名称) ,并使用 Python 解释器运行它。确保不要调用应用程序 jnius.py,因为它会与 Pyjnius 本身发生冲突:
1 |
|
如果返回类型不是本机类型,Pyjnius使用Java反射为您提供一个新的autoclass()。让我们看看这个例子。
1 |
|
我们只声明了第一个System类,但是我们能够自然地使用所有静态字段和方法。继续深入:
1 |
|
递归反射始终为您提供一个反映返回的 Java 对象的适当对象。
3 API接口
3.1 反射类
class jnius.JavaClass
Java反射类的基础。这个想法是对这个 JavaClass 进行子类化,添加一些 JavaMethod、 JavaStaticMethod、 JavaField、 JavaStaticField,然后就完成了。
您至少需要定义__javaclass__
属性,并将__metaclass__
设置为 MetaJavaClass
。
最简单的定义应该像下面一样:
1 |
|
__metaclass__
属性必须设置为MetaJavaClass
, 否则,声明的所有方法/字段将不会链接到 JavaClass
确保选择正确的元类说明符。在 Python 2中有 __metaclass__类属性,在Python 3中有一个新的语法类 Stack (JavaClass,metaclass = MetaJavaClass)。
__javaclass__
表示格式为‘ org/lang/Class’(例如‘ Java/util/Stack’)而不是‘ org.lang. Class’的 Java 类名。
__javaconstructor__
如果没有设置,我们假设缺省构造函数没有参数。否则,它可以是构造函数的所有可能签名的列表。例如,String 类的反射如下:
1 |
|
class jnius.JavaMethod
方法的反射。
__init__
(signature, static=False)
创建一个 Java 方法的反射。签名是 JNI 格式的。例如:
1 |
|
与该方法关联的名称是从 JavaClass 本身的声明中自动设置的。
可以通过 javap -s找到签名。例如,如果您想获取 java.util.Stack可用的签名。:
1 |
|
class jnius.JavaStaticMethod
静态java方法的反射。
class jnius.JavaField
Java字段的反射。
__init__
(signature, static=False)
创建Java字段的反射。签名格式为JNI。
与方法关联的名称是在JavaClass本身的声明中自动设置的。
class jnius.JavaStaticField
Java静态字段的反射。
class jnius.JavaMultipleMethod
可以从多个签名调用的Java方法的反射。例如,可以调用String类中的getBytes方法
1 |
|
声明该方法的例子:
1 |
|
然后,当您尝试访问此方法时,它将根据您使用的参数类型选择可用的最佳方法。在内部,我们为每个可用的签名计算一个“匹配”分数,并选择最好的一个。在不深入细节的情况下,得分计算看起来是这样的:
- 直接类型匹配为 + 10
- 间接类型匹配(如使用 float 作为 int 参数)为 + 5
- 类型未知的对象(JavaObject)是 + 1
- 否则,将视为错误情况,并返回 -1
3.2 反射函数
jnius.autoclass
返回一个表示从名称传递的类的 JavaClass。名称必须使用 a.b.c 格式,而不是 a/b/c 格式。
1 |
|
Autoclass 也可以表示嵌套的 Java 类:
1 |
|
有时候,Java 类包含一个 Python 关键字成员(如 from、 class 等)。您需要使用 getattr ()来访问这个成员,然后您就可以调用它:
1
2
3
from jnius import autoclass
func_from = getattr(autoclass('some.java.Class'), 'from')
func_from()还有一种特殊情况,就是作为 Some Class.getClass ()的结果或者在 _javaclass_ python 属性中可以找到 Some Class.class 文本。
警告:目前SomeClass.getClass()返回一个不同的Python对象,因此要安全地比较Java中是否有相同的类,请使用A.hashCode() == B.hashCode()。
3.3 Python中Java类的实现
class jnius.PythonJavaClass
从 Python 类创建 Java 类的基础。这使我们可以完全用 Python 实现 Java 接口。
实际上,您将创建一个模仿声明的 _ _ javainterface _ _ 列表的 Python 类。当您将这个类的一个实例交给 Java 时,Java 只会接受它并按照声明调用接口方法。在底层,我们捕获调用,并将其重定向为使用声明的 Python 方法。
您的类将充当 Java 接口的代理。
您至少需要定义__javainterfaces__
属性,并使用 java_method()
装饰符声明 java 方法。
请注意,这个地方不支持静态方法和静态字段。
例如,您可以在Python中实现Java/util/ListIterator接口:
1 |
|
__javainterfaces__
需要代理的 Java 接口的列表,格式为‘ org/lang/Class’(例如‘ Java/util/Iterator’) ,而不是‘ org.lang.Class’。
__javacontext__
指示要使用哪个类装入器,“ system”还是“ app”。默认值是“ system”。
- 默认情况下,我们假设您将实现JavaAPI中声明的 Java 接口。它将使用“ system”类装入器。
- 在 android 上,您在 APK 中提供的所有 Java 接口都不能通过系统类装入器访问,而是通过应用程序线程类装入器访问。因此,如果你想实现一个类,你已经在你的应用程序的接口,使用’应用程序’。
`jnius.java_method(java_signature, name=None)
用于 PythonJavaClass
的装饰函数。java_signature
必须与接口的所需签名相匹配。默认情况下,该方法的名称将是 Python 方法的名称。在使用相同 Java 方法名称的多个签名的情况下,仍然可以强制执行。
例如:
1 |
|
另一个具有相同 Java 方法名称但有两个不同签名的示例:
1 |
|
3.4 Java签名格式
Java 签名有一种特殊的格式,一开始可能很难理解。我们来看看细节。签名的格式如下:
1 |
|
签名任何部分的所有类型都可以是:
L < Java class > ; = 表示类型为 < Java class > 的 Java 对象
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,仅对返回类型可用
所有类型都可以有指示数组的[前缀。返回类型可以是 V 或空。
像这样的签名:
1 |
|
当您用Python实现Java时,Java方法的签名必须匹配。Java提供了一个名为javap的工具来获取任何Java类的签名。
1 |
|
3.5 JVM选项和类路径
在调用 import jnius 之前需要设置 JVM 选项,因为在 VM 启动之后不能更改这些选项。为此,你可以:
1 |
|
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.
如果用这些函数设置了类路径,它将覆盖任何 CLASSPATH 环境变量。多个选项或路径条目应作为多个参数提供给 add_ 和 set _ 函数。如果未提供类路径且未设置 CLASSPATH,则路径默认为‘.’ 。这个功能在Android上是不可用的。
3.6 Pyjnius和线程
jnius.detach()
每次在 Python 中创建本机线程并使用 Pyjnius 时,对 Pyjnius 方法的任何调用都会强制将本机线程附加到当前 JVM。但是你必须在离开线程之前把它分离出来,而 Pyjnius 不能为你做这件事。
例如:
1 |
|
如果你不这样做,它将在 dalvik 和 ART/Android 上崩溃:
1 |
|
或者:
1 |
|