除錯支付寶離線認證介面遇到的問題總結
阿新 • • 發佈:2019-02-02
通過 android的JNI呼叫支付寶離線認證庫本地介面時,我欲返回一個類的例項,但是卻報了幾個錯,最後查出來了原因。在此總結下。
錯誤一 :E/dalvikvm﹕JNI ERROR (app bug): accessed stale local reference
,jclass is an invalid local reference AllocObject
原因是 android的垃圾回收機制問題,新版本已經不允許全域性使用findclass出來的區域性引用了,
會被GC回收掉。所以必須new成全域性引用才行。
需要注意的地方是:
static jclass myClass;
,一,需要把jclass宣告為靜態的 。二,注意這個地方,用NewGlobalRef把引用設為全域性的。寫在
JNIEXPORT jint JNI_OnLoad(JavaVM* vm,void中。*reserved)
jclass tmp;
tmp = (*env)->FindClass(env,"com/example/ndktest/VerifyResponse"); if( tmp == NULL ) { ALOGE("%d..Can't find class %s!\n",__LINE__, "com/example/ndktest/VerifyResponse"); return -1; } myClass = (jclass)(*env)->NewGlobalRef(env,tmp);
錯誤二: E/dalvikvm﹕ JNI ERROR (app bug): accessed stale global reference 0x474e386e
需要注意的地方是:
(*env)->SetObjectField(env,obj,cardNo,"12345678");這樣的寫法是錯誤的,正確應該是:
(*env)->SetObjectField(env,obj,cardNo,(jstring)(*env)->NewSringUTF(env,"1234567890"));給欲返回的字串賦值,需要用(*env)->NewSringUTF(env,"1234567890"
//========================================================================
以下為我欲通過JNI返回給java層呼叫的支付寶離線校驗的結果,類
VerifyResponse
的定義:
package com.example.ndktest; /** * Created by yang on 2017/7/8. */ public class VerifyResponse { byte[] cardData; int cardDataLength; String cardNo=""; String cardType; int errorCode; String record; String uid; public byte[] getCardData() { return this.cardData; } public int getCardDataLength() { return this.cardDataLength; } public String getCardNo() { return this.cardNo; } public String getCardType() { return this.cardType; } public int getErrorCode() { return this.errorCode; } public String getRecord() { return this.record; } public String getUid() { return this.uid; } public void setCardData(byte[] paramArrayOfByte) { this.cardData = paramArrayOfByte; } public void setCardDataLength(int paramInt) { this.cardDataLength = paramInt; } public void setCardNo(String paramString) { this.cardNo = paramString; } public void setCardType(String paramString) { this.cardType = paramString; } public void setErrorCode(int paramInt) { this.errorCode = paramInt; } public void setRecord(String paramString) { this.record = paramString; } public void setUid(String paramString) { this.uid = paramString; } public String toString() { return "uid:" + getUid() + "," + "record:" + getRecord() + "," + "cardType:" + getCardType() + "," + "cardNo:" + getCardNo() + "," + "cardData:" + getCardData() + "," + "cardDataLength:" + getCardDataLength() + "," + "errorCode:" + new Integer(getErrorCode()).toString(); } }
在android的應用層,宣告如下:
public static native VerifyResponse verifyQrcode(byte[] qrCodeOfByte, String paramPos, int amount_cent);
在用c寫的JNI 層,寫法如下:
jobject JNICALL jni_verify_pos_qrcode(JNIEnv *env,jobject thiz,jbyteArray qrCodeOfByte, jstring paramPos, jint amount_cent) { int ret = 0; jboolean b; jsize qrcode_len; const unsigned char* qrcode= NULL; const char* pos_param = NULL; LOGD("%s>>> ..%s..%d..enter",LOG_TAG,__FUNCTION__,__LINE__); // jclass clazz = (*env)->FindClass(env,"com/example/ndktest/VerifyResponse"); // if(clazz == 0){ // return (void*)-100; // } jobject obj = (*env)->AllocObject(env,myClass); jfieldID errorCode = (*env)->GetFieldID(env,myClass,"errorCode","I"); if(errorCode == 0){ return (void*)-101; } jfieldID cardNo = (*env)->GetFieldID(env,myClass,"cardNo","Ljava/lang/String;"); if(cardNo == 0){ ret = -104; goto END; } jfieldID uid = (*env)->GetFieldID(env,myClass,"uid","Ljava/lang/String;"); if( uid == 0){ ret = -105; goto END; } pos_param= (char*)((*env)->GetStringUTFChars(env,paramPos, &b)); qrcode = (unsigned char*)(*env)->GetByteArrayElements(env,qrCodeOfByte, NULL); qrcode_len = (*env)->GetArrayLength(env,qrCodeOfByte); if(qrcode_len > 512){ ret = -103; goto END; } LOGI("===========校驗二維碼開始================\n"); //拼裝驗證請求 VERIFY_REQUEST_V2 verify_request; //裝入二進位制格式的二維碼 verify_request.qrcode = qrcode; //裝入二進位制二維碼長度 verify_request.qrcode_len = qrcode_len; //裝入pos_param verify_request.pos_param = pos_param; //裝入本次消費金額 如果生成離線記錄時還無法確定消費金額 裝入0(單位:分) verify_request.amount_cent = amount_cent; VERIFY_RESPONSE_V2 verify_response; verify_response.uid = (char*)malloc(17); verify_response.uid_len = 17; verify_response.record = (char*)malloc(2048); verify_response.record_len = 2048; verify_response.card_no = (char*)malloc(32); verify_response.card_no_len = 32; verify_response.card_data = (unsigned char*)malloc(128); verify_response.card_data_len = 128; verify_response.card_type = (char*)malloc(16); verify_response.card_type_len = 16; /** * 呼叫介面驗證二維碼的有效性 */ ret = verify_qrcode_v2(&verify_request, &verify_response); /** * 處理返回的結果 */ if(ret != SUCCESS){ switch(ret){ case MALFORMED_QRCODE: LOGI("二維碼格式錯誤!請提示使用者二維碼錯誤。\n"); break; case QRCODE_INFO_EXPIRED: LOGI("二維碼過期!請提示使用者重新整理二維碼。\n"); break; case QRCODE_KEY_EXPIRED: LOGI("二維碼金鑰過期!請提示使用者聯網後重新整理二維碼再使用。\n"); break; case POS_PARAM_ERROR: LOGI("商戶傳入的pos_param錯誤,請檢查傳入的pos_param。\n"); break; case QUOTA_EXCEEDED: LOGI("單筆額度超限!請提示使用者由於額度限制無法過閘機。\n"); break; case NO_ENOUGH_MEMORY: LOGI("記憶體不足,極端錯誤,請檢查程式執行空間是否足夠。\n"); break; case QRCODE_DUPLICATED: LOGI("二維碼重複!驗證失敗。\n"); break; case SYSTEM_ERROR: LOGI("系統異常!請聯絡支付寶技術人員。\n"); break; default: break; } LOGI("二維碼校驗結束!驗證失敗,不放行!\n"); LOGI("===========驗證二維碼 結束================\n"); goto END; } LOGI("從二維碼中獲取到的uid: %s\n", verify_response.uid); LOGI("驗證成功後,返還的離線記錄: %s\n", verify_response.record); LOGI("二維碼中的卡型別為: %s\n",verify_response.card_type); LOGI("二維碼中的卡號為: %s\n", verify_response.card_no); bytes_to_hex_string(print_buf, sizeof(print_buf), verify_response.card_data, verify_response.card_data_len); LOGI("二維碼中的二進位制卡資料(hex string形式):%s\n", print_buf); /** * 1.商戶可以根據uid判斷是否為同一使用者重複交易 */ /** * 2.商戶可以根據qrcode判斷是否為重複二維碼 * 此判斷也可以放在校驗二維碼前執行,商戶可以自行選擇 */ /** * 3.商戶需要根據卡型別、卡號、卡資料 綜合判斷該卡的合法性、以及是否受理該卡 * 請商戶保留 可受理 的離線記錄 */ LOGI("驗證成功,請放行!\n"); LOGI("response ok!\n"); (*env)->SetObjectField(env,obj,cardNo,"12345678"); //(*env)->SetObjectField(env,obj,cardNo,(jstring)(*env)->NewSringUTF(env,"1234567890")); LOGI("response ok1!\n"); END: free(verify_response.uid); free(verify_response.record); free(verify_response.card_no); free(verify_response.card_data); free(verify_response.card_type); (*env)->SetIntField(env,obj,errorCode,ret); LOGI("response ok2!\n"); //(*env)->SetObjectField(env,obj,cardNo,"12345678"); (*env)->SetObjectField(env,obj,cardNo,(jstring)(*env)->NewStringUTF(env,"12345678")); LOGI("response ok3!\n"); (*env)->ReleaseStringUTFChars(env, paramPos, pos_param); (*env)->ReleaseByteArrayElements(env,qrCodeOfByte,(jbyte*)qrcode,0); return obj; }
//定義批量註冊的陣列,是註冊的關鍵部分 static const JNINativeMethod gMethods[] = { {"qrcode_test", /* func2是在java中宣告的native函式名 */ "()I", /* "()V"是函式的簽名,可以通過javah獲取。*/ (void*)jni_check_qrcode_test }, { "initPosVerify", "(Ljava/lang/String;Ljava/lang/String;)I", (void*)jni_init_pos_verify }, { "verifyQrcode", "([BLjava/lang/String;I)Lcom/example/ndktest/VerifyResponse;", (void*)jni_verify_pos_qrcode } }; // extern "C" { JNIEXPORT jint JNI_OnLoad(JavaVM* vm,void *reserved) { JNIEnv *env =NULL; jint result = -1; static const char* kClassName="com/example/ndktest/Test"; jclass clazz,tmp; LOGD("%s>>> ..%s..%d..enter",LOG_TAG,__FUNCTION__,__LINE__); if( (*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_4) != JNI_OK ) { return result; } clazz = (*env)->FindClass(env,kClassName); if( clazz == NULL ) { ALOGE("%d..Can't find class %s!\n",__LINE__, kClassName); return -1; } tmp = (*env)->FindClass(env,"com/example/ndktest/VerifyResponse"); if( tmp == NULL ) { ALOGE("%d..Can't find class %s!\n",__LINE__, "com/example/ndktest/VerifyResponse"); return -1; } myClass = (jclass)(*env)->NewGlobalRef(env,tmp); if( (*env)->RegisterNatives( env,clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0]) ) != JNI_OK ) { ALOGE("Failed registering methods for %s!\n", kClassName); return -1; } LOGD(">>> ..%s..%d.exit",__FUNCTION__,__LINE__); return JNI_VERSION_1_4; } // }