2016-03-25 14 views
2

私はJNIを使​​って、C++コードからいくつかのJavaクラスを呼び出そうとしています。今日私は私のプログラマーで非常に奇妙な行動を経験しました。私は、C++コードがJava側で作業を終えるまで待たずに、なぜかわからないと思う。JNI:C++はJavaを非同期呼び出しですか?

(共有オブジェクトライブラリ内の)C++コードは、独自のC++スレッドで実行されています。すでに稼働しているJavaアプリの既存のJavaVMを使用しています。 JavaアプリケーションがJNI_Onloadで共有オブジェクトライブラリをロードしている間にフェッチされたVMおよびClassLoaderへの参照。ここで私は、私はC++のスレッドでJNIを使​​用して作成されたJavaオブジェクトのJavaメソッドを呼び出します。

env->CallVoidMethod(javaClassObject, javaReceiveMethod, intParam, byteParam, objectParam); 
uint32_t applicationSideResponseCode = getResponseCode(objectClass, objectParam, env); 
resp.setResponseCode(applicationSideResponseCode); 
std::string applicationData = getApplicationData(serviceResultClass, serviceResultObject, env); 
resp.setData(applicationData); 

のJava javaReceiveMethodがデータベースにアクセスし、objectParamに格納されているいくつかのapplicationDataをフェッチしています。残念ながら、C++コードはjavaクラスが処理を完了する前にapplicationDataをフェッチしました。 applicationDataがnullであり、JNIがクラッシュします。どうして? CallVoidMethodが非同期で実行されているというOracleの文書は見つかりませんか?

編集

私も例外は、Javaメソッドで発生していないことを確認しました。 C++がデータにアクセスしようとしている間にJavaが依然としてビジーであることを除いて、すべてうまくいくようです。

編集

私はJavaアプリケーションをデバッグする場合は2つのスレッドが示されていることを確認することができます。メインスレッドでは、javaReceiveMethodを実行しており、1つのスレッドはapplicationDataをフェッチしています。どうすればこの問題を解決できますか?データが利用可能になるまで、2番目のスレッドでアイドル状態になっていますか?上記のように、私はコードを呼び出すその後

jmethodID javaClassConstructor= env->GetMethodID(javaClass, "<init>", "()V"); 
        jobject serviceObject = env->NewObject(javaClass, serviceConstructor); 
jobject javaClassObject = env->NewObject(javaClass, javaClassConstructor); 

:私は、私が呼び出したいJavaクラスの新しいオブジェクトを作成しています私のC++コードで

編集

。より多くのデバッグの後、私はこのメソッドがThread-2という名前のスレッドで呼び出されると言うことができます(これはC++スレッドかJNIの新しいスレッドかわかりません)。それは間違いなくjavaのメインスレッドではありません。しかし、この方法の作業は中断されます。つまり、コードをデバッグするとデータがすぐに設定されることがわかりますが、次のデバッグステップではgetApplicationDataメソッドが実行されます(これはC++が呼び出している場合にのみ発生します)。

編集 私は呼んでJavaメソッド:

public int receive(int methodId, byte[] data, ServiceResult result){ 
     log.info("java enter methodID = " + methodId + ", data= " + data); 

     long responseCode = SEC_ERR_CODE_SUCCESS; 

     JavaMessageProto msg; 
     try { 
      msg = JavaMessageProto (data); 
      log.info("principal: " + msg.getPrincipal());  
      JavaMessage message = new JavaMessage (msg); 

      if(methodId == GET_LISTS){ 
       //this is shown in console 
       System.out.println("get lists");     
       responseCode = getLists(message); 
       //this point is not reached 
       log.info("leave"); 
      }   
      //[... different method calls here...] 
      if(responseCode != SEC_ERR_CODE_METHOD_NOT_IMPLEMENTED){ 
       //ToDoListMessageProto response = message.getProtoBuf(); 
       JavaMessageProto response = JavaMessageProto.newBuilder() 
         .setToken(message.getToken()) 
         .setPrincipal(message.getPrincipal()).build(); 
       byte[] res = response.toByteArray();     
       result.setApplicationData(response.toByteArray()); 
      } 
      else{ 
       result.setApplicationData(""); 
      } 

     } catch (InvalidProtocolBufferException e) { 
      responseCode = SEC_ERR_CODE_DATA_CORRUPTED; 
      log.severe("Error: Could not parse Client message." + e.getMessage()); 
     }    

     result.setResponseCode((int)responseCode); 
     return 0; 
    } 

第2の方法は

public long getLists(JavaMessage message) { 
log.info("getLists enter"); 

String principal = message.getPrincipal(); 
String token = message.getToken(); 

if(principal == null || principal.isEmpty()){   
    return SEC_ERR_CODE_PRINCIPAL_EMPTY; 
}  
if(token == null || token.isEmpty()){  
    return SEC_ERR_CODE_NO_AUTHENTICATION; 
}  

//get user object for authorization 
SubjectManager manager = new SubjectManager(); 
Subject user = manager.getSubject(); 

user.setPrincipal(principal); 
long result = user.isAuthenticated(token); 
if(result != SEC_ERR_CODE_SUCCESS){ 
    return result; 
} 
try { 
    //fetch all user list names and ids   
    ToDoListDAO db = new ToDoListDAO();  
    Connection conn = db.getConnection();  
    log.info(principal + " is authenticated"); 
    result = db.getLists(conn, message); 
    //this is printed 
    log.info(principal + " is authenticated");   
    conn.close(); //no exception here 
    message.addId("testentry"); 
    //this not   
    log.info("Fetched lists finished for " + principal); 
} catch (SQLException e) { 
    log.severe("SQLException:" + e.getMessage()); 
    result = SEC_ERR_CODE_DATABASE_ERROR; 
}   

return result; 
} 
+0

問題の説明を明確にしてください。最初に "javaReceiveMethod"がC++スレッド上で実行され、それがメインスレッド上で実行されたとしますか? Btwでは、メソッド呼び出しは同期して実行されます。 –

+0

私は自分の答えを更新し、それを明らかに説明するつもりです。しかし、現時点では、JNIが何をしているのか、私は非常に混乱しています。 –

+0

呼び出しているJavaメソッドが表示されていません。 – Michael

答えて

3

CallVoidMethodでは同期的に実行されます。

はたぶん、あなたは、あなたがそれ自身のC++のスレッドで実行されているC++ JNI例外チェック?:(共有オブジェクトライブラリの)

env->CallVoidMethod(javaClassObject, javaReceiveMethod, intParam, byteParam, objectParam); 
if(env->ExceptionOccurred()) { 
    // Print exception caused by CallVoidMethod 
    env->ExceptionDescribe(); 
    env->ExceptionClear(); 
} 

C++のコードを使用しますか?、C++側のexcepionを持っています。すでに稼働しているJavaアプリの既存のJavaVMを使用しています。

現在のスレッドを仮想マシンに接続しているかどうかは不明です。 がAttachCurrentThreadコールから着信していることを確認してください。ここに例があります:How to obtain JNI interface pointer (JNIEnv *) for asynchronous calls

+0

糸がついています。私はvm-> AttachCurrentThread((void **)&env、NULL)を呼び出します。 –

+0

実際にJavaコードでは例外がスローされましたが、Java側では表示されませんでした。 'addId'メソッドは' NullPointerException'を引き起こしました。私の開発環境ではJavaがこのような例外を表示すると私は思っていましたが、このような奇妙な動作はこのようなバグによって引き起こされるとは思えませんでした。申し訳ありませんが\t不便です。 'env-> ExceptionOccured'を指摘してくれてありがとうございます。私はまだJNIを初めて使っているので、このように例外をキャッチできるかどうかはわかりません。 –

関連する問題