Pyjnius: PyJnius vs JPype

作成日 2020年07月16日  ·  27コメント  ·  ソース: kivy/pyjnius

私はJPypeの筆頭著者です。 JPypeのドキュメントを更新する一環として、JPypeの代替コードのリストにPyJniusを追加しました。 残念ながら、PyJniusで2時間遊んだ後、JPypeに対するPyJniusの利点のように見えるものを思い付くことができませんでした。 プロキシ、カスタマイザー、多次元配列処理、javadoc統合、GC処理、バッファー、科学的コード統合(numpy、matplotlibなど)、呼び出し元に依存するメソッド、ドキュメント、さらにはほとんどの場合の実行速度から私が見たすべての側面が現在カバーされていますJPypeではPyJniusよりも完全に。 ただし、JPypeの作成者である私は、PyJniusチームの側面ではなく、自分が大切にしている側面を見ているのかもしれません。 このプロジェクトの利点をより明確に説明できますか? このプロジェクトの価値提案は何ですか、そのターゲットオーディエンスは何ですか、そして代替案がまだカバーしていないことを行うために何をターゲットにしていますか?

最も参考になるコメント

私は約2年前にすべてのJavaブリッジコードに連絡を取りました。 残念ながら、PyJniusコードは、私の検索では表示されなかったため、見落とされたようです。 前回素晴らしい技術プレスリリースを行ったページを探していて、2つのプロジェクトについて議論しているブログに出くわしたことを除けば、このラウンドでもそれを見逃していたでしょう。 同じ地域で2年間、別のアクティブなプロジェクトを見逃した理由はわかりませんが、それは明らかに私のせいでした。

JPypeが他の主要なブリッジコードであるPy4Jと混同されたようです。 彼らは、ソケットを使用してすべてを行い、それに伴う利点と欠点の両方を備えています。 同様に、プロジェクトが私の要件を満たしていないことがわかりました。

私はアンドロイドをサポートするために何が必要かについての研究をしていません。 しかし、私がいくつかの技術仕様を持っていれば、それは可能であるはずです。 ネイティブJNIとPythonのCAPIへの昔ながらの呼び出し以外に私たちが行っていることは何もありません。

アプローチに関しては、JPypeはJNIを排他的に使用して、「startJVM()」コマンドを使用してJVMをPythonに結合します。 次のバージョン(2.0)でも、Java内からPythonを起動できる逆の機能が提供されます。 これは、レイヤーアプローチを通じて行われます。 フロントエンドとして機能するすべての高レベルクラスを保持するPythonレイヤー、エントリポイントを保持する基本クラスを備えたCPythonプライベートモジュール、すべての型変換とマッチングを処理し、ネイティブモジュールとして機能するC ++バッカーレイヤーがあります。 Javaライブラリ、およびすべてのユーティリティタスク(オブジェクトの存続期間の保持、スライスと例外のバッキングクラスの作成、およびjavadocエクストラクタ/レンダリング)を行うJavaライブラリ用。

8年前、JPypeは少し混乱していました。 RubyとPythonの両方をサポートしようとしていたため、C ++レイヤーはラッパーの絡み合いであり、フロントエンドはすべてPythonであったため、非常に低速でした。 また、numpyサポートをコンパイルして別のオブジェクトが返される可能性があるため、戻りタイプで分岐しました。 ネイティブのPythonオブジェクトとJavaオブジェクトが異なるプロキシとして機能するには、JExceptionのような多くのアダプタクラスが必要でした。 しかし、私がプロジェクトに参加してから3年で、これらの問題はすべて解決されました。 JPypeの(私にとっての)2つの主要な目標は、物理学者がJavaを使用できるようにプログラミングに精通し、科学的なPythonコードと高レベルで統合できるように十分に単純な構文を提供することです。 これを行うには、Javaでサポートされているオブジェクトに、「すべてのフリル」CPythonオブジェクトラッパーを設定します。 Javaプリミティブ配列を変換するのではなく、Javaプリミティブ配列を新しいネイティブPython型にします。これは、numpyと同じエントリポイントをすべて実装し、これらはすべてメモリバッファ転送によってサポートされます。 したがって、 list(jarray)またはnp.array(jarray)呼び出すことで、高速な変換が可能になります。

これをAndroidに適合させるには、起動シーケンスを作り直す必要があり、内部ライブラリのロードに使用するサンクコードを従来のJNIモデルに置き換える必要があります。 私はすでに次のバージョンでサンクコードを削除しているので、後者はすでに満たされています。 前者のみが必要になります。

私が見ることができるアプローチの主な違いは、PyJniusが配列を変換することです。 これは、大きな配列を前後に渡す(変換されないことが多い)ことが好ましいJPypeスタイルである科学的コーディングには非常に禁止されているように見えます。 変換を要求するという決定は、値渡しや参照渡しなどのオプションを強制しますが、複数の引数を呼び出す場合は、すべての引数に対して1つのポリシーを選択するかのように、より大きな問題が発生する可能性があります。 また、多次元配列の処理が困難になります。 ( obj.method(1, jnius.byref(list1), list2)ように使用されるアダプタークラスは、それが達成できれば、より良い制御を提供したと思います)。 また、GCリンケージや発信者に敏感な方法など、JPypeが解決した問題はたくさんあります。 他に何もない場合は、JPypeコードを調べて、使用できる良いアイデアがあるかどうかを確認してください。

全てのコメント27件

お問い合わせいただきありがとうございます。

jpypeを見ていないのは何年も前のことなので、覚えていないかもしれませんが、共有メモリではなくサーバー(IPC)を介してJVMと通信するという別のアプローチを使用していました(ただし、readmeは反対のことを示唆しています、そして、コードを一目見ただけでそれを反証するヒントは見当たらないので、おそらく完全に間違っています)、このアプローチは、たとえば、Androidでは機能しませんでした(これは、pyjniusを開発する主な理由の一種でしたが一部の人々はデスクトッププラットフォームでそれを使用します)。 一方、JPypeコードベースでのAndroidのサポートについては、jni.hがAOSPから取得されているように見えるため、 nativeディレクトリの内容でない限り、あまりヒントがありません。

しかし正直なところ、プロジェクトが開始されたとき、私たちはJPypeをまったく認識していませんでした。それは、kivyサポートのために特定のAndroidクラスにインターフェイスするためにjniコードを手動でコーディングする必要からのステップアップにすぎませんでした。 @titoは、後でそれについて知ったときに比較するためにいくつかの掘り下げを行ったと思いますが、切り替えを試みない特定の理由を彼が見たかどうかは覚えていません。

やあ。 JPypeの名前は覚えていますが、使用できるものを検索したとき、8年前に正直に使用されなかった理由を思い出せません:)最初の唯一の目標はAndroidと通信できるようにすることでしたAPIですが、P4Aプロジェクトがその時点で行っていたように中間RPCサーバーを使用していません。

私は約2年前にすべてのJavaブリッジコードに連絡を取りました。 残念ながら、PyJniusコードは、私の検索では表示されなかったため、見落とされたようです。 前回素晴らしい技術プレスリリースを行ったページを探していて、2つのプロジェクトについて議論しているブログに出くわしたことを除けば、このラウンドでもそれを見逃していたでしょう。 同じ地域で2年間、別のアクティブなプロジェクトを見逃した理由はわかりませんが、それは明らかに私のせいでした。

JPypeが他の主要なブリッジコードであるPy4Jと混同されたようです。 彼らは、ソケットを使用してすべてを行い、それに伴う利点と欠点の両方を備えています。 同様に、プロジェクトが私の要件を満たしていないことがわかりました。

私はアンドロイドをサポートするために何が必要かについての研究をしていません。 しかし、私がいくつかの技術仕様を持っていれば、それは可能であるはずです。 ネイティブJNIとPythonのCAPIへの昔ながらの呼び出し以外に私たちが行っていることは何もありません。

アプローチに関しては、JPypeはJNIを排他的に使用して、「startJVM()」コマンドを使用してJVMをPythonに結合します。 次のバージョン(2.0)でも、Java内からPythonを起動できる逆の機能が提供されます。 これは、レイヤーアプローチを通じて行われます。 フロントエンドとして機能するすべての高レベルクラスを保持するPythonレイヤー、エントリポイントを保持する基本クラスを備えたCPythonプライベートモジュール、すべての型変換とマッチングを処理し、ネイティブモジュールとして機能するC ++バッカーレイヤーがあります。 Javaライブラリ、およびすべてのユーティリティタスク(オブジェクトの存続期間の保持、スライスと例外のバッキングクラスの作成、およびjavadocエクストラクタ/レンダリング)を行うJavaライブラリ用。

8年前、JPypeは少し混乱していました。 RubyとPythonの両方をサポートしようとしていたため、C ++レイヤーはラッパーの絡み合いであり、フロントエンドはすべてPythonであったため、非常に低速でした。 また、numpyサポートをコンパイルして別のオブジェクトが返される可能性があるため、戻りタイプで分岐しました。 ネイティブのPythonオブジェクトとJavaオブジェクトが異なるプロキシとして機能するには、JExceptionのような多くのアダプタクラスが必要でした。 しかし、私がプロジェクトに参加してから3年で、これらの問題はすべて解決されました。 JPypeの(私にとっての)2つの主要な目標は、物理学者がJavaを使用できるようにプログラミングに精通し、科学的なPythonコードと高レベルで統合できるように十分に単純な構文を提供することです。 これを行うには、Javaでサポートされているオブジェクトに、「すべてのフリル」CPythonオブジェクトラッパーを設定します。 Javaプリミティブ配列を変換するのではなく、Javaプリミティブ配列を新しいネイティブPython型にします。これは、numpyと同じエントリポイントをすべて実装し、これらはすべてメモリバッファ転送によってサポートされます。 したがって、 list(jarray)またはnp.array(jarray)呼び出すことで、高速な変換が可能になります。

これをAndroidに適合させるには、起動シーケンスを作り直す必要があり、内部ライブラリのロードに使用するサンクコードを従来のJNIモデルに置き換える必要があります。 私はすでに次のバージョンでサンクコードを削除しているので、後者はすでに満たされています。 前者のみが必要になります。

私が見ることができるアプローチの主な違いは、PyJniusが配列を変換することです。 これは、大きな配列を前後に渡す(変換されないことが多い)ことが好ましいJPypeスタイルである科学的コーディングには非常に禁止されているように見えます。 変換を要求するという決定は、値渡しや参照渡しなどのオプションを強制しますが、複数の引数を呼び出す場合は、すべての引数に対して1つのポリシーを選択するかのように、より大きな問題が発生する可能性があります。 また、多次元配列の処理が困難になります。 ( obj.method(1, jnius.byref(list1), list2)ように使用されるアダプタークラスは、それが達成できれば、より良い制御を提供したと思います)。 また、GCリンケージや発信者に敏感な方法など、JPypeが解決した問題はたくさんあります。 他に何もない場合は、JPypeコードを調べて、使用できる良いアイデアがあるかどうかを確認してください。

@Thrameos最近マージしようとしていることの1つは、Java関数型インターフェースにPythonラムダを使用することです。 https://github.com/kivy/pyjnius/pull/515を参照して

JPypeは、1.0.0以降のFunctionalインターフェイスからのラムダをサポートしています。 これは、3月に1.0にプッシュされた30日間の30回のプルの一部でした。

JPypeは長い潜伏期間を持っています。 当初は2004年に開始され、2007年まで実行されていました。その後、ユーザーのグループが2015年頃にPython 3に移植するために復活させたため、大きな後押しがありました。その後、2017年に、0.6から搭載された国立研究所で使用できるようになりました。 3から0.7.2。 その期間中、すべての努力は、インターフェースを提供するコアテクノロジーの改善と強化に集中していました。 しかし、それは2回目のコアの書き直しの後、3月にようやく完了したので、ようやく1.0.0をプッシュすることができました。 それ以来、「30泊30プル」のウィッシュリストキャンペーンで「欠けていた」ものをすべて追加してきました。 作業が多すぎるために実装できなかったすべてのバックログが最終的にクリアされました(問題が50から20に減少し、ユーザーに何が必要かを尋ねるなど)。 そのため、以前のバージョンでは見つからなかった機能がいくつかあり、現在は利用可能になっている可能性があります。 私はほとんどの機能リクエストを1週間足らずで回しており、残っているのはビッグ3(リバースブリッジ、Pythonでのクラスの拡張、2番目のJVMを開始する機能)だけです。

JavaがPythonを呼び出し、Pythonライブラリのスタブを生成してJavaネイティブライブラリとして使用できるようにするリバースブリッジコードを完成させるために、2か月から6か月の長い努力を重ねているため、プロジェクトは眠りに戻ります。 ASMを使用してJavaクラスをオンザフライで構築し、Pythonのネイティブサポートを実現できるようにします。 Jythonのようにまだ完全には統合されていませんが、おそらく十分に近いため、大きな違いはありません。

詳細な説明に感謝します、そして確かに、どちらかといえば、私たちが使用できるアイデアが確かにあります、そしてコードを研究する価値があります、以前のコードを見て、私が見たものはコードの品質と構造の両方で非常にきれいに見えますプロジェクトなので、すべての作業をおめでとうございます。 あなたは私のPy4Jとの取り違えについて正しいです、私がJPypeを見たとき、それはあなたが説明する厄介な状態にあったに違いありません、そしてそれを使うことはこの時点でPyJNIusよりずっと複雑だったに違いありません。

@ hx2Aがパフォーマンスを改善する方法を検討し、Python型への変換をオプションにしたため(使い捨てリストをJavaに渡して取得するなど)、値の受け渡し/変換に関するあなたのポイントは非常に真実であり、ここでも最近の議論を引き起こしましたJavaリストに変換され、Javaで変更されたかどうかにかかわらず、Pythonに戻されてガベージコレクションされるのは確かに最適ではありませんでした。キーワード引数を使用することを犠牲にして、少なくとも2番目の部分を回避できます。 javaはそれらをサポートしていないので、署名の衝突はありませんが、構文的には確かにもう少しうるさいです)。

JPypeとPyJNIusの考えられる違いについては、Pythonクラスを使用してJavaインターフェイスを実装し、それらをJavaに渡してコールバックとして使用できますが、Javaクラスを拡張する場合は、実際にJavaバイトコードを生成する必要があります。 Pythonから、いくつかのandroid apiを使用する必要があるため、現時点ではカバーできません。コメントから正しく推測できるかどうかはわかりませんが、Javaクラスで呼び出しを行う機能がない可能性があります。このようなPythonコード(インターフェースを使用)。

JPypeはPythonでインターフェースを実装できます。 通常のPythonクラスにデコレータを追加するだけです。

from java.util.function import Consumer

@jpype.JImplements(Consumer)
class MyConsumer:
   @jpype.JOverride
   def apply(self, obj):
       pass

JVMが開始される前にクラスが定義されている場合は、 @ JImplementsで文字列を使用します。複数のインターフェースを一度に実装できますが、すべてのメソッドが実装されているかどうかがチェックされます。 主な違いは、JPypeがオーバーロードされたメソッドではなくディスパッチメソッド(すべてのオーバーロードが同じメソッドに送られる)を使用することです。 これは、JavaとPythonの両方からメソッドを呼び出せるようにするためです。 必要な機能であれば、個別のオーバーロードを追加できますが、誰もそれを要求していません。

(編集:Python継承を使用しなかった理由は、これが追加された時点で、多くのメタクラスの問題を引き起こしたPython 2をまだサポートしていたためです。クラス拡張が配置されたら、さらにクリーンアップします。宣言時に一度評価すると、よりクリーンになりました。)

同じシステムを使用して、クラスのカスタマイザー(dunder?)を実装します

@jpype.JImplementationFor("java.util.ArrayList")
class ArrayListImpl:
    def __getitem__(self, i):
        return self.get(i)
    @jpype.JOverride
    def addAll(self, list):
        # Decide if we need to convert or can call directly.
        ...

また、アノテーションを使用して、「すべてのPythonPathオブジェクトがjava.io.Fileに変換される」などの暗黙的なコンバーターを定義します。

 @jpype.JConversion("java.io.File", instanceof=pathlib.PurePath)
 def _JFileConvert(jcls, obj):
       Paths = jpype.JClass("java.nio.file.Paths")
       return Paths.get(str(obj))

明らかに、この種のロジックを使用すると、特別なCメソッドよりも少し遅くなりますが、読み取り可能性と柔軟性が高く保たれます。 ボトルネックであることが証明されているクリティカルパスを必要に応じてCに戻しています。

また、物事をクリーンにするためのシンタックスシュガーも提供しますMyJavaClass@obj => MyJavaClassにキャスト(Java相当の(MyJavaClass)obj )またはcls=JInt[:] =>配列型を作成します( cls=int[].class )またはa=JDouble[10][5] =>マルチディム配列( double[][] a = new double[10][5] )を作成します。

私はJPypeからクラスを拡張するためのプロトタイプに取り組んでいます。 一部のSwingクラスや他のAPIがクラスの拡張を必要とするのと同じ問題があります。 これまでに作成した解決策は、オーバーライドされたメソッドごとにHashMapを使用して拡張クラスを作成することです。 そのエントリポイントのハッシュマップに何もない場合は、それをsuperに渡します。それ以外の場合は、プロキシメソッドハンドラを呼び出します。 しかし、JavaがJavaプロキシメソッドを経由するのではなく、実際にPythonメソッドを処理できるように、リバースブリッジが完了した後でこれを実装するのが最も簡単であると判断しました。 ですから、プロトタイプが機能するまでにはまだ約6か月の休みがあります。 epypjブランチ(私の名前はリバースブリッジ)を見て、JavaからPythonへの呼び出しがどのように機能するか、およびASMを使用してJavaクラスをオンザフライで作成する呼び出し元を生成するパターンを確認できます。

ガベージコレクションの管理に関しては、その作業を行うJPypeはほとんどありません。 1つは、Pythonオブジェクトの存続期間をJavaオブジェクトにバインドするJPypeReferenceQueue(native / java / org / jpype / ref / JPypeReferenceQueue)です。 これは、Javaが一定期間Pythonの概念にアクセスする必要があるバッファなどを作成するために使用されます。 2つ目は、グローバル参照を使用して、PythonがJavaオブジェクトをスコープ内に保持できるようにすることです。 これには、ガベージコレクターリンク(native / common / jp_gc.cpp)が必要です。このリンクは、いずれかのシステムがGCをトリガーするのをリッスンし、特定の条件(プールのサイズ、相対的な増加)時に他のシステムにpingを送信します。 最後のプロキシは弱い参照を使用する必要があります。そうしないとループが形成されるためです(プロキシはJavaの半分への参照を保持し、Javaの半分はPythonの実装を指すため)。 最終的には、エージェントを使用してPythonがJavaをトラバースできるようにするつもりですが、それは道のりです。

私はAndroidではなくデスクトップでpyjniusを使用している人の1人です。 プロジェクトの構築を始めたとき、JPypeについて知りませんでしたが、違いが何であるかを確認するために調査を行いました。

pyjniusのユニークな機能の1つは、呼び出し元が保護されたプライベートメソッドとフィールドを含めるかどうかを決定できることです。 私の好みはパブリックのみですが、非パブリックのフィールドとメソッドを使用可能にすることは有用であるという議論を理解しています。

私のプロジェクトにとって、パフォーマンスは非常に重要です。 私は以下のクラスでいくつかのテストを行いました:

package org.pkg;

public class MyClass {

  public MyClass() {
  }

  public int number = 42;

  public float add1(float x, float y) {
    return x + y;
  }

  public float add2(float x, float y) {
    return x + y;
  }

  public float add2(int x, int y) {
    return x + y;
  }
}

JPypeの場合:

In [1]: import jpype
   ...: import jpype.imports
   ...: jpype.startJVM()
   ...: from org.pkg import MyClass
   ...: myInstance = MyClass()
   ...:

In [2]: %timeit myInstance.number
640 ns ± 2.65 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [3]: %timeit myInstance.add1(10.3, 20.5)
2.13 µs ± 24.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [4]: %timeit myInstance.add2(10.3, 20.5)
2.19 µs ± 9.41 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

pyjniusの場合:

In [1]: import jnius

In [2]: MyClass = jnius.autoclass('org.pkg.MyClass')

In [3]: myInstance = MyClass()

In [4]: %timeit myInstance.number
161 ns ± 0.104 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [5]: %timeit myInstance.add1(10.3, 20.5)
1.04 µs ± 8.16 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [6]: %timeit myInstance.add2(10.3, 20.5)
2.71 µs ± 11.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Pyjniusは、オーバーロードされたメソッドを除いて、かなり高速です。 メソッドがオーバーロードされたときに呼び出すメソッドを決定する方法に関する注記を比較する必要があります。 Pyjniusにはこのスコアリングメカニズムがあり、多くのオーバーヘッドが追加されるようです。 JPypeはその決定をはるかに迅速にします。

最後に、ベンチマークの目的で:

In [9]: def add(x, y):
   ...:     return x + y
   ...:

In [10]: %timeit add(10.3, 20.5)
82.9 ns ± 0.187 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

もちろん、数µsの違いは些細なことですが、何千もの小さな呼び出しを非常に迅速に行う場合は、それが加算されます。これを行う必要があります。

JPypeとnumpyの統合は非常に素晴らしく、使いやすいです。 研究者がこれを使用して、複雑な構文なしで大きな配列をJavaライブラリに渡すスクリプトを作成する方法がわかります。 また、大きな配列を渡す必要があります。これは、 tobytes()と、バイトを受信できる特別なJavaコードを使用して行いますが、それは明らかにそれほど便利ではありません。

残念ながら、JPypeの速度は、ある種の見通しです。 JPypeは、悪いことを防ぐために非常に防御的です。つまり、重要なオーバーヘッドがたくさんあります。 つまり、たとえばJava呼び出しが行われるたびに、スレッドが接続されていることを確認するため、IDEなどの外部スレッドから呼び出された場合にセグメンテーション違反が発生することはありません。 私のローカルユーザーグループはすべて科学者であるため、エントリポイントはすべて、かなり恐ろしい相互配線から非常に保護されています。 何かがそれらにセグフォールトした場合(それがどれほどクレイジーであったとしても)、私は失敗しました。 (これは、意図的に悪いオブジェクトを作成することを含む、1500のテストを説明します。)

第二に、個々のアクションの速度は、実行される作業のごくわずかな違いで大きく異なる可能性があります。 あなたが挙げた些細な例は、最悪のケースの1つでした。 アクセスしているフィールドのタイプによっては、実際にアクセス速度が変わる場合があります。

速度の例では、intフィールドを要求しています。

  • PyJniusでは、オブジェクトルックアップの記述子を作成し、フィールドにアクセスして、新しいPython longを作成し、それを返します。
  • JPypeでは、オブジェクトルックアップの記述子を作成し、フィールドにアクセスし、新しいPython longを作成してから、Java intのラッパー型を作成し、Python longメモリをJIntにコピーします(Pythonには派生物を作成する方法がないため)整数クラスを直接)、次にスロットをJava値でバインドし、最後に結果のJIntを返します。

したがって、フィールドへのアクセスで速度ベンチマークを実行するのと同じくらい些細なことでさえ、実際にはそれほど些細なことではありません。
1つはPythonlongを返し、もう1つは実際のJava整数(Java変換ルールに従う)を返しました。これは、別のオーバーロードされたメソッドに渡され、適切にバインドされます。 ラッパー型を返す作業は、単にPython型を返すだけではないため、速度に大きな違いがあります。

いくつかの異なるフィールドタイプをテストして、これを実証しようとしました。 残念ながら、オブジェクトフィールドをテストしたところ、jniusはコード「harness.objectField = harness」でsegfaultを実行しました。 その特定のクロス配線が問題を引き起こした理由はわかりませんが、失敗しました。 私はJPypeの速度にはあまり興味がありませんでしたが、重大な違反者を排除することで、呼び出しの速度が3〜5倍になり、特定の配列アクセスの速度が300倍になりました。 しかし、おそらく私はどの領域を改善できるかを検討して確認する必要があります。 安全性を取り除いたり、返品契約を取り除いたりせずに、PyJniusのような必要最低限​​のものにまで減らすことができるかどうかは疑問です(私にはできません)。 せいぜい10-30%のスピードアップがまだ可能です、

プライベートフィールドとプロテクトフィールドにアクセスする機能については、確かに可能です。 私は、ユーザーがオブジェクトを直接公開するのではなく、リフレクションまたは他の内部アクセス方法を使用することを好みます。 そのようなものを提供する必要がある場合は、公開されていないフィールドを含む_privateというフィールドを作成する可能性があります。 JPypeは、タイプごとに1つのクラスラッパーしか提供しないため、きめ細かい制御の方法はあまりありません。 そのため、プライベートアクセスを持つクラスを作成してから、同じタイプの2番目のオブジェクトを作成し、プライベートが公開されないようにすることを選択できませんでした。 私は文字列変換を使ってその道を進みましたが、あるライブラリが1つのポリシーを選択し、他のライブラリが別のポリシーを選択して非互換性が発生するという惨事でした。

配列リストを使用していくつかのテストを実行しました。

import jpype
import timeit
jpype.startJVM()
ArrayList = jpype.JClass("java.util.ArrayList")

def pack():
    ja = ArrayList()
    for i in range(1000):
        ja.add(i)

def iter(ja):
    u = 0
    for i in ja:
        u+=i

def access(ja):
    u = 0
    for i in range(len(ja)):
        u+=ja.get(i)

def access2(ja):
    u = 0
    for i in range(len(ja)):
        u+=ja[i]


ja = ArrayList()
for i in range(1000):
   ja.add(i)

print("Pack arraylist %e"%( timeit.timeit("pack()", globals=globals(), number=1000)/1e6))
print("Iterate arraylist %e"%(timeit.timeit("iter(ja)", globals=globals(), number=1000)/1e6))
# Get is a direct call
print("Access(get) arraylist %e"%(timeit.timeit("access(ja)", globals=globals(), number=1000)/1e6))
# [] is emulated
print("Access([]) arraylist %e"%(timeit.timeit("access2(ja)", globals=globals(), number=1000)/1e6))

JPype

パック配列リスト2.768904e-06
arraylist5.208071e-06を繰り返します
Access(get)arraylist 4.037985e-06
Access([])arraylist 4.690264e-06

ジュニウス

パック配列リスト3.322248e-06
arraylist4.099314e-06を繰り返します
Access(get)arraylist 5.653444e-06
Access([])arraylist 7.762727e-06

非常に異なる機能を提供している場合を除いて、おそらく些細な違いであると言う以外は、あまり一貫した話ではありません。 メソッドにアクセスするだけの場合、それらはかなり似ている可能性があります。 事前変換されたJPypeである配列の受け渡しは、100倍速く、リストとタプルの変換は2倍遅くなります(現在、ベクトルアクセスを使用していないか、タプルに特別なバイパスがあります)。 したがって、最終的な収益はコーディングスタイルによって異なりますが、どちらか一方を使用するとはるかに高速になる可能性があります。 しかし、私のユーザーは通常、スピードよりも使いやすさと防弾構造のためにJPypeを選択します。 (ああ、冗談です!JPypeがたまたまGoogleで見つけた最初のリンクだったので、彼らはインターネットから偶然出くわす可能性があります。)

JPypeがメソッドバインディングをどのように行うかについては、その詳細を開発者/ユーザーガイドに記載する必要があります。 メソッドバインディングは、Java仕様で指定されたルールに従ってディスパッチのメソッドのリストを事前にソートすることから始まります。これにより、何かが他の何かを隠した場合、リストの最初に表示されます(コードはnative / java / org / jpypeにあります。ディスパッチが最初に作成されるときに、Javaユーティリティクラスを使用してソートを実行します)。 さらに、各メソッドには、どのメソッドが別のメソッドを非表示にするかの優先リストが与えられます。 解決は、最初に各引数をチェックして、引数に「Javaスロット」があるかどうかを確認することから始まります。 Javaスロットは、変換を必要としない既存のオブジェクトを指しているため、一致する前にこれらを邪魔にならないようにすることは、暗黙的なルールではなく直接的なルールを使用できることを意味します。 次に、タイプに基づいて引数を4つのレベル(正確、暗黙、明示、なし)に照合します。 明示的なものをショートカットし、次のメソッドにジャンプするものはありません。 正確になったら、プロセス全体をショートカットして呼び出しにジャンプします。 一致するものがある場合は、特異的結合が少ないメソッドが非表示になります。 非表示になっていない暗黙の一致が2つ見つかった場合は、TypeErrorに進みます。 すべての一致がなくなると、変換ルーチンが実行されます。 次に、Pythonグローバルロックを解放して呼び出しを行い、グローバルロックを再取得します。 戻り型が検索され、返された型に基づいて新しいPythonラッパーが作成されます(共変の戻り値を使用するため、返される型はメソッドからの型ではなく、最も派生したものになります)。 可変個引数にはいくつかの複雑さがありますが、これはオーバーロードの数とほぼ線形ですが、事前に構築されたテーブルは、foo(double、double)を試行する前にfoo(long、long)を試行し、(long)をヒットすることを意味します、long)は、Javaメソッド解決ルールのために、double、doubleがすべて一致するのを防ぎます。 実装できるスピードアップはまだいくつかありますが、追加のキャッシュテーブルが必要になります。

2017年にプロジェクトを開始したとき、ショートカット付きの注文システムを継承しました。オーバーヘッドの大部分をプッシュするために、キャッシュとJavaスロットを非表示にするメソッドを追加しました。

メソッドの実行パスを最適化しました。 JPypeの改訂番号は次のとおりです。

パック配列リスト2.226081e-06
arraylist4.082152e-06を繰り返します
Access(get)arraylist 2.962606e-06
Access([])arraylist 3.644642e-06

私のローカルユーザーグループはすべて科学者であるため、エントリポイントはすべて、かなり恐ろしい相互配線から非常に保護されています。 何かがそれらにセグフォールトした場合(それがどれほどクレイジーであったとしても)、私は失敗しました。

はい、segfaultsは恐ろしいものであり、pyjniusを使い始めたときに何百ものsegfaultsを取得しました。 安全性の問題を解決してコードに組み込んだのかもしれませんが、長い間何も得られていません。 これで、すべてが確実に機能します。 私はあなたのユースケースを理解しています。 ユーザーがJavaオブジェクトを直接操作してさまざまなJavaライブラリでデータ分析を行う科学者である場合、セグメンテーション違反によりすべての作業が失われます。 JPypeは、エンドユーザーがPythonを介してJavaオブジェクトを直接操作する科学的な作業を行うために設計されているようです。 ただし、pyjniusの主な使用例は異なり、Androidとの通信です。 その場合、安全性の問題は開発者の問題であるため、安全性と速度について異なる選択を行うことが適切である可能性があります。

私は「これらの正方形をこの順序で踏む限り安全です」という大ファンではないことを認めます。 JPypeの作業を開始したとき、コードをローカルグループに渡すことができるように、すべてのエントリポイントを防弾するのに1年近くかかりました。 それ以来、APIアーマーをさらに2年間追加しました。 JVMがロードに失敗する(解決するのが非常に難しい)少数のまれな人々を除いて、JPypeが本番コード標準に引き上げられているため、残りの問題はほとんどありません。

速度と安全性のトレードオフとしては、速度は素晴らしいですが、安全な操作を軽視して速度を上げている場合、通常、ほとんどのユーザーにとっては不十分なトレードオフです。 プロトタイピングコードを好転させる場合でも、本番システムを作成する場合でも、作業を停止してセグメンテーション違反を回避しようとすることは、ユーザーが直面してはならない気晴らしです。

誰かがAndroidエミュレーターでJPypeをテストする方法のいくつかの例を教えてくれるなら、私は必要な変更を加えることについて見ることができます。

androidで使用するには、pyjniusをpython-for-androidによって構築されたpythonディストリビューションのアートとしてパッケージ化し(多くの場合、buildozerをより簡単なインターフェイスとして使用しますが、同じです)、このディストリビューションを出荷するpythonアプリケーションを構築します。次に、ユーザーがアプリを実行したときに、PythonコードでpyjniusまたはPythonディストリビューションに組み込まれているその他のPythonライブラリをインポートできます。

したがって、最初のステップは、jpypeをディストリビューションにコンパイルすることです。これは、通常は(常にではありませんが)純粋なPythonではないライブラリを構築する方法をp4aに説明するには、「レシピ」が必要です。pyjnius用のレシピはhttps://github.com/kivy/python-for-android/blob/にあります。例としてp4a.local_recipesに設定buildozer.specあなたがフォークにのpython-ため-アンドロイドを必要としないので、要件のためのレシピを見つけることができるディレクトリを宣言するためにレシピを使用してもらいます。

buildozerを使用することをお勧めします。これは、 https: //buildozer.readthedocs.io/en/latest/installation.html#targeting -androidを自動化し、ローカルレシピを設定して試してみることができるためです。 最初のビルドは、Pythonとarmのいくつかの依存関係をビルドする必要があるため、時間がかかります。また、そのためにandroidndkとsdkをダウンロードする必要があります。 おそらくアプリのデフォルトのkivyブートストラップを使用して、アプリのような「hello world」を作成できます。これは、jpypeをインポートして、コードの結果をラベルに表示するか、printを使用してlogcatに表示するだけです。 androidエミュレーターでkivyがどれだけうまく動作するかを覚えておいてください、私はそれを使用したことはありませんが、一部のユーザーは使用したと思います。物事を表示するためのボトルサーバー、これまでで最もテストされているので、最初にkivyのものを試してみます。

Android用にビルドするには、LinuxまたはOSXマシン(VMは問題なく、Windows 10のWSLは問題ありません)が必要です。

python-for-androidのjpypeレシピの作業を開始する場合は、今後発生する可能性のあるディスカッションのために、進行中のPRを開くことを歓迎します。 それがうまくいくなら、特にそれが確かにいくつかの長年のpyjnius制限を解決することができるならば、それは素晴らしいでしょう。 スレッドの前半で説明したように、pyjniusは基本的にkivyを使用するためのコア要件をカバーしていますが、これを大幅に超える十分な開発力がありません。

@inclement jpype-project / jpype#799でAndroidポートのPRを設定しました。 残念ながら、ここからどこに行けばいいのかよくわかりません。 実際には正しいビルドパスではないgradleを実行しようとしているようです。

実行する必要のあるアクションは次のとおりです。

  • [x]すべてのjpype / *。pyファイルをビルド(またはそれらのコンパイル済みバージョン)に含めます。
  • [x] native /build.xmlでApacheantを実行し、結果のjarファイルをアクセス可能な場所に配置します。
  • [x] jarファイル(または同等のもの)をビルドに含めます。
  • [x] C ++コードをnative / commonおよびnative / pythonから_jpypeという名前のモジュールにコンパイルして、ビルドに含めます。
  • [x]インタラクティブシェルを起動するだけのmain.pyファイルを含めて、今のところこれを手動でテストできるようにします。
  • []将来的には、動的に作成されたクラスをロードできるように、Android用に「ASM」またはそのように機能するものを含める必要があります。
  • [x] C ++コードにパッチを適用して、カスタムブートストラップを使用してJVMとコンパニオンjarファイルをロードし、すべてのネイティブメソッドを接続するようにします。
  • [] Androidで機能するものでjvmfinderにパッチを適用すると、「startJVM」がメインの開始時ではなく自動的に呼び出されます。
  • [] org.jpypeにパッチを適用して、jarナビゲーションシステム(インポートの動作方法)がAndroidで機能できるようにします。

私はいくつかのドキュメントを調べましたが、これを達成する方法については何も目立っていませんでした。 すべてをメインモジュールの下に配置しないため、プロジェクトのレイアウトは通常とは多少異なります(実際には、システムを構成する3つのモジュールを構築しているためです。jpype、_jpype、およびorg.jpype)。カスタムレシピが必要になる可能性があります。これらすべてのアクションを実行するだけでなく、gradleの実行などの望ましくないパターンを無効にします(私にはわからない有用なことをしている場合を除く)。

実際には正しいビルドパスではないgradleを実行しようとしているようです。

Gradleは、APKをパッケージ化する最終ステップとして使用されるビルドツールであり、おそらくjpypeを含めることとは関係ありません。

すべてのjpype / *。pyファイルをビルド(またはそれらのコンパイル済みバージョン)に含めます。

一般に、jpypeが基本的に通常のPythonモジュールとして実行される場合、最初のレシピの試行はおそらく手間のかかる作業のほとんどを実行します- CppCompiledComponentsPythonRecipepython setup.py build_extpython setup.py install実行するようなことをしますNDK環境を使用します。 これにより、アプリ内に含めるために構築されているpython環境内にjpypepythonパッケージがインストールされます。

ビルドにjarファイル(または同等のもの)を含めます。

これはおそらくレシピが実行する必要のある追加の手順です。それは、jarファイル(または必要なもの)をpython-for-androidが構築しているAndroidプロジェクト内の適切な場所にコピーすることです。

C ++コードをnative / commonおよびnative / pythonから_jpypeという名前のモジュールにコンパイルして、ビルドに含めます。

これがsetup.pyによって処理される場合、これはすでに機能しているはずですが、微調整が必​​要な場合があります。 そうでない場合は、コンパイルコマンドをレシピに含めることができます(他のレシピでself.get_envを使用して行われるように、適切なenv変数を設定することでAndroid環境用にビルドすることができます)。

カスタムブートストラップを使用してJVMとコンパニオンjarファイルをロードし、すべてのネイティブメソッドをフックするように、C ++コードにパッチを適用します。

適切なAndroidJNIインターフェース関数の使用に応じて、この部分がかなり単純であることを願っています。 たとえばこのパッチで示されているように、さまざまなヘルパーライブラリがさまざまなラッププレを提供するため、適切な条件付きコンパイルを行うのではなく、pyjniusにパッチを適用することで、これを少しハックな方法で行います。 この複雑さはあなたに影響を与える必要はありません、あなたはちょうど正しいアンドロイドAPI関数を呼び出すことができます。

アンドロイドで機能するものでjvmfinderにパッチを当てると、「startJVM」はメインの開始時ではなく自動的に呼び出されます。

私はJVMに精通していないので、実際に知ることはできませんが、Androidでは常に既存のjvmにアクセスする必要があり、新しいインスタンスを開始することはできないと思います。 それは問題になりますか(またはそれはただ間違っていますか?)?

native /build.xmlでApacheantを実行し、結果のjarファイルをアクセス可能な場所に配置します。

なじみがないため、これについてもわかりません。これは単なる内部jpypeビルドステップですか? Javaバージョンがどのように相互作用するかはわかりませんが、ここではシステムネイティブのantを使用しても問題ないと思います。

buildozerがsetup.pyを尊重しないという警告が表示されたため、上から直接gradleステップにスキップしました。 したがって、これらの中間ステップを手動で追加する必要があります。 setup.pyは、フックファイルを作成してjarファイルをdllとマージするなど、一連のカスタムコンパイル手順を実行します。これは、ここでは適用できない可能性が高いため、スキップしても問題ありませんでしたが、cppファイルとjavaファイルも見つかりませんでした。これはsetup.pyで定義されています。 問題の一部は、setup.pyが大きすぎて、モジュールsetupextに分割する必要があったことです。

最初に、プロセスを1回実行してログを表示できるように、クリーンなコマンドを実行する方法を理解する必要があると思います。 (プレイ中に初めてピースミールを実行したので、きれいなログがありません。)また、スレッドのトピックについてもっと詳しく知ることができるように、この会話をPRに移動する必要があります。

FWIWAndroid以外のプロジェクトのコアをJPypeに移植することができました。 2つの小さな問題または違いがありました:

  1. classpathでkwarg jpype.startJVM()関係なく、私は何をすべきか、無視しないでいるようです。 ただし、 addClassPath()関数は機能しました。 クラスパスの順序を気にする開発者は、 jnius_config.add_classpath()使用方法と比較していくつかの調整を行う必要がある場合があります。

  2. Java Interfacesの実装はうまく機能しますが、少し異なります。 私のコードには、Python文字列のリストを返す関数がありました。 振り返ってみると、おそらく各文字列をJava文字列に変換する必要がありましたが、それを考えず、これを機能させるためにインターフェイス関数定義にJavaオブジェクトのリストを返させました。 これはpyjniusで正常に機能し、Python文字列をJavaオブジェクトのサブクラスに効果的にします。 これはJPypeでは機能しませんが、JPypeのJStringクラスで文字列を変換することで、簡単に修正できました。

実装されたメソッドに@JOverrideを記述することは、 @java_method('(Ljava/lang/String;[Ljava/lang/Object;)V')ようなコードよりもはるかに簡単です。

これらの2つの問題に対処すると、基本的にすべてが正常に機能しました。 変更する必要のあるバイト配列を渡すコードがいくつかありますが、JPypeはそれを適切にサポートしています。 また、JPypeの暗黙的な型変換は非常に便利で、私がいたるところに散らばらなければならなかった多くのデコレータを置き換えることができます。

@Thrameosここでのあなたの決意を尊重します。 JPypeをAndroidで動作させるための幸運を祈ります。

コメントありがとうございます。

クラスパスで何が間違っているのかはわかりませんが、Pythonを開始した場所であると推測する必要があります。 時々人々はモジュールからJVMを開始します、そして開始ディレクトリに関してJavaパスとPythonパスのルールが異なるので、それはjarを見逃す原因となる可能性があります。 (この問題に関するユーザーガイドのセクションがあります)。

私は毎日クラスパス機能を使用しており、これはテストパターンの一部です。 したがって、クラスパスが尊重されなかった場合、かなり大きな失敗が発生します。 とはいえ、複雑なパターンを機能させるために必要な場所と絶対パスの魔法のセットを見つけるのに苦労するのはあなたが最初ではないでしょう。

これは、開発エリアごとにPyディレクトリからテストシステムを起動する例です。

import jpype
import os

devel = os.path.dirname(__file__)
devel = os.path.join(devel, '..', '..')
devel = os.path.abspath(devel)  # Notice that I converted the path to absolute so that it doesn't matter where the
# PWD of Java will be when this script is called.   Otherwise, if I import this from a different location it will use the
# original PWD and Java will find nothing in the classpath.

classpath = [
    '%s/gov.llnl.math/dist/*' % devel,
    '%s/gov.llnl.rdak/dist/*' % devel,
    '%s/gov.llnl.rnak/dist/*' % devel,
    '%s/gov.llnl.rtk/dist/*' % devel,
    '%s/gov.llnl.rtk.gadras/dist/*' % devel,
    '%s/gov.llnl.rtk.response/dist/*' % devel,
    '%s/gov.llnl.utility/dist/*' % devel,
    ]

jpype.startJVM(classpath=classpath, convertStrings=False)

相対パスと絶対パスは機能しますが、これはどこから開始するかによって大きく異なります。 addClassPathには、すべてのパスが呼び出し元の場所を基準にしていることを確認するための特別な魔法があります。 クラスパスのキーワード引数にも同じロジックが必要だと思います。

文字列のリストを返す際の問題がわかります。 トリガーされる動作に関しては、リターンのタイプによって異なります。 メソッドがString[]を返すように宣言されている場合、戻るときに各Python文字列をJavaに強制的にオンにすることを期待します。 メソッドがList<String>を返すと宣言された場合、Javaジェネリックがメソッドを削除してList<Object>なるため、問題が発生します。 JPypeがリストをどのように表示するかに応じて、変換を試みる場合と試みない場合がありますが、これは動作を定義する必要があるため、確認します。 安全な解決策は、返品のためにすべてのアイテムを変換できるリスト内包表記です。

クラスパスで何が間違っているのかはわかりませんが、Pythonを開始した場所であると推測する必要があります。 モジュールからJVMを起動する人もいます

それが私がやっていたことです!

私は毎日クラスパス機能を使用しており、これはテストパターンの一部です。 したがって、クラスパスが尊重されなかった場合、かなり大きな失敗が発生します。 とはいえ、複雑なパターンを機能させるために必要な場所と絶対パスの魔法のセットを見つけるのに苦労するのはあなたが最初ではないでしょう。

はい、これはあなたがすぐに気付くものであり、私がこの問題を抱えた最初の人ではないと思いました。 コードサンプルをありがとう、それは役に立ちます。 その結果、コードにいくつかの改善を加えました。

安全な解決策は、返品のためにすべてのアイテムを変換できるリスト内包表記です。

それはまさに私がしたことであり、今では正しく機能します。 ありがとう!

数年前、私はpy4jとjniusを比較したところ、jniusの方がはるかに高速であることがわかりました。

今まで私鋸jpypeは場所で言及したとき、私はいつもそれが言及していたと仮定- http://jpype.sourceforge.net/ 、それはそれは(変なふうに新しいプロジェクトを見ていない)メンテナンスされていないように見えた原因から離れていました

このスレッドを読んで、ベンチマークをもう一度試しました(速度の主なテストケースは、PMMLファイルを読み取ってスコアリングすることです)。そして、jpypeのパフォーマンスがかなり高いことがわかりました。
いくつかの結果:
https://gist.github.com/AbdealiJK/1dd5b7677435ba22f9ab3e26016bb3e7

# jpype
# createjvm: 0.550s
# loadmodel: tot=1.466451 max=1.064521s avg=0.014665s
# fields   : tot=0.019881 max=0.009795s avg=0.000199s
# score    : tot=0.033356 max=0.023338s avg=0.000334s

# jnius
# createjvm: 0.249s
# loadmodel: tot=1.773011 max=1.385274s avg=0.017730s
# fields   : tot=0.039058 max=0.012234s avg=0.000391s
# score    : tot=0.067590 max=0.031904s avg=0.000676s

# py4j
# createjvm: 0.222s
# loadmodel: tot=0.616913 max=0.027464s avg=0.006169s
# fields   : tot=0.699152 max=0.026426s avg=0.006992s
# score    : tot=0.389583 max=0.017620s avg=0.003896s

公平を期すために、JPypeのフロントエンドAPIは2020年3月まで(CPythonではなく)Pythonで記述されていました。したがって、提供されたものに対してかなり遅いことを示す古いベンチマークがたくさんあります。 メモリ管理で解決しなければならないバックエンドの問題が非常に多く、Rubyやマルチスレッドなどをサポートするために多層ラッパー全体を削除したため、その速度が最後に取り組む必要がありました。

この時点で、かなり高速になるはずです。 ただし、他のパッケージと比較して追加の作業が必要なものを見つけた場合は、問題にメモを入れてください。 リターンコントラクトのためにいくつかの制限がありますが、呼び出し、配列転送、およびメモリバッファリングのプライマリパスは、この時点でかなり処理されています。

JPypeでも良い結果が出ています。 特に、numpy配列との統合から良い価値を得ています。 以前はできなかった新機能を追加することができ、有意義な方法でパフォーマンスを向上させることができました。

誰かがkivy-remote-shellツールを更新して、現在のPython3とbuildozerでビルドできるようにし、手順を段階的に改善することができれば、JPypeの移植作業に役立ちます。大きく。 起動とテストの段階までに必要なすべての手順を完了しました。 しかし、ブートストラッププロセスを完了するための環境がなければ、進歩を遂げることは困難です。 今週末にリモートシェルツールの更新を再試行する(そしておそらく成功する)か、kivyの知識が豊富な利害関係者がこの前提条件のタスクを完了してから、週末を過ごして、最も完了する資格のある技術作業を完了することができます。 。 私は他の人を助けるために自由に時間を提供していますが、それは限られたリソースであり、Androidの移植作業で行う作業は、他の多くの人も興味を持っているJavaブリッジからのPythonの遅延です。

アンドロイドの移植作業が、違いを処理できるようにコアコードを数週間作り直したPyPyの移植作業の邪魔にならないようにできることを願っていますが、オブジェクトシステムのわずかな違いによってエラーが発生するという技術的な問題が発生しました。そして、生成されたコードで生成されたエラーレポートをデバッグする方法を追跡するのを手伝ってくれる人を見つけることができませんでした。 こぼれたミルクについては泣きませんが、JPypeコードを他の意味のある方法に改善するためにそのすべての努力が費やされましたが、結局、JPypeを使用したいユーザーは高くて乾燥したままでした。 この努力が失敗した場合、私はそれに戻るのですべてが失われるわけではありませんが、キューの最後に何かが来ると、誰かが私を助けるために時間をかけることができない限り、私は6ヶ月間それに戻ることを強く迫られます。

利害関係者の進捗状況の更新。

pythonforandroid内からJPypeを正常に起動し、基本的な機能をテストすることができました。 JVMの違いにより、一部の高度な機能が使用できない場合がありますが、JPypeの大部分はAndroidプラットフォームで使用できるようになると思います。 この移植には、buildozerおよびpythonforandroidプロジェクトへのアップグレードが必要だったため、しばらく時間がかかりました(したがって、ソースをよく読んで助けを求めました)。 ある週末にプロセスの最も難しい部分を完了することができるように応答してくれたここの開発者に感謝します。 あなたの入力なしではそれは不可能だったでしょう。 関連する変更をPRとして入れましたが、PRのバックログを見ると、検討に入るまでに少し時間がかかるかもしれません。 必要な主要な技術仕様が揃ったので、それを統合して、晩秋のカレンダーで名目上JPype、1.2あたりの実用的なリリースコードを作成できるはずです。 ユーザーの関心が高い場合は前に進めることができますが、他のプロジェクトにとって重要な機能であるJavaのPythonと競合しています。

誰かが作業を加速するのを手伝いたい場合、次の難しいステップは、ビルドを実行し、エミュレーターをロードし、テストベンチを実行できるように、部分ビルドシステムですべてを配置したDockerイメージを構築する方法を理解することです。紺碧のパイプライン(または他のCIシステム)上のAndroidの。 何が機能し、何が機能しないかを検出できるCIが機能するようになると、安定したソフトウェアとしてデプロイできるようになります。 これをJPypeプロジェクトに格納する必要があるのか​​、それとも別のandroidテストプロジェクトを作成する必要があるのか​​がわかりません。

このページは役に立ちましたか?
0 / 5 - 0 評価