1. 程式人生 > >除錯支付寶離線認證介面遇到的問題總結

除錯支付寶離線認證介面遇到的問題總結

通過 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;
}
// }