okhttp、okio添加參數(addParams)出現的java.lang.IllegalArgumentException:Unexpected code point

這是我們項目,收集到的,用戶出現過不少次的一個問題。根據堆棧發現是出現在用戶修改自己的個人資料時出現的。

項目的網絡框架用的是okhttp,在bugly上的樣子如下圖

<Image_1>

查看堆棧信息,找到拋出這個異常的地方,在okhttp的okio這個jar包的Buffer類中,的writeUtf8CodePoint()這個方法中

<Image_2>

Buffer類的源碼如下

@Override public Buffer writeUtf8CodePoint(int codePoint) {
    if (codePoint < 0x80) {
      // Emit a 7-bit code point with 1 byte.
      writeByte(codePoint);

    } else if (codePoint < 0x800) {
      // Emit a 11-bit code point with 2 bytes.
      writeByte(codePoint >>  6        | 0xc0); // 110xxxxx
      writeByte(codePoint       & 0x3f | 0x80); // 10xxxxxx

    } else if (codePoint < 0x10000) {
      if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
        throw new IllegalArgumentException(
            "Unexpected code point: " + Integer.toHexString(codePoint));//可能拋出異常的地方 A
      }

      // Emit a 16-bit code point with 3 bytes.
      writeByte(codePoint >> 12        | 0xe0); // 1110xxxx
      writeByte(codePoint >>  6 & 0x3f | 0x80); // 10xxxxxx
      writeByte(codePoint       & 0x3f | 0x80); // 10xxxxxx

    } else if (codePoint <= 0x10ffff) {
      // Emit a 21-bit code point with 4 bytes.
      writeByte(codePoint >> 18        | 0xf0); // 11110xxx
      writeByte(codePoint >> 12 & 0x3f | 0x80); // 10xxxxxx
      writeByte(codePoint >>  6 & 0x3f | 0x80); // 10xxxxxx
      writeByte(codePoint       & 0x3f | 0x80); // 10xxxxxx

    } else {
      throw new IllegalArgumentException(
          "Unexpected code point: " + Integer.toHexString(codePoint));//可能拋出異常的地方 B

    }

    return this;
  }
從源碼,可以看到,有兩個地方可能出現這種情況,而這個方法的代碼,是進行Utf-8編碼時,根據規則

大於0xd800 並且小於0xdfff的範圍,是做其他用的(詳情可參看Unicode編碼以及Utf-8等相關知識),而大於0x10ffff是超出了Unicode的編碼範圍的。


現在我們需要知道這個方法在哪裏被調用的,以及codePoint是什麼東西,怎麼來的?

最終我們找到了okhttp3這個jar包中的HttpUrl這個類中的canonicalize這個方法中,該方法源碼如下

 static void canonicalize(Buffer out, String input, int pos, int limit, String encodeSet,
      boolean alreadyEncoded, boolean strict, boolean plusIsSpace, boolean asciiOnly) {
    Buffer utf8Buffer = null; // Lazily allocated.
    int codePoint;
    for (int i = pos; i < limit; i += Character.charCount(codePoint)) {
      codePoint = input.codePointAt(i);
      if (alreadyEncoded
          && (codePoint == '\t' || codePoint == '\n' || codePoint == '\f' || codePoint == '\r')) {
        // Skip this character.
      } else if (codePoint == '+' && plusIsSpace) {
        // Encode '+' as '%2B' since we permit ' ' to be encoded as either '+' or '%20'.
        out.writeUtf8(alreadyEncoded ? "+" : "%2B");
      } else if (codePoint < 0x20
          || codePoint == 0x7f
          || codePoint >= 0x80 && asciiOnly
          || encodeSet.indexOf(codePoint) != -1
          || codePoint == '%' && (!alreadyEncoded || strict && !percentEncoded(input, i, limit))) {
        // Percent encode this character.
        if (utf8Buffer == null) {
          utf8Buffer = new Buffer();
        }
        utf8Buffer.writeUtf8CodePoint(codePoint);
        while (!utf8Buffer.exhausted()) {
          int b = utf8Buffer.readByte() & 0xff;
          out.writeByte('%');
          out.writeByte(HEX_DIGITS[(b >> 4) & 0xf]);
          out.writeByte(HEX_DIGITS[b & 0xf]);
        }
      } else {
        // This character doesn't need encoding. Just copy it over.
        out.writeUtf8CodePoint(codePoint);
      }
    }
  }
通過該方法 的源碼,我們就可以知道codePoint是如何來的了,如此我們就找到了出現這種問題的原因,可能是用戶輸入某些還不能被識別的字符,那麼我們爲了避免用戶應用出現崩潰的情況,我們可以進行提前規避。做法如下


在出現問題的地方,對每個參數進行如下代碼的檢查

private boolean checkIsUnLawful(String[] input){
        if (input != null && input.length> 0){
            for (int i = 0; i < input.length; i++) {
                String length = input[i];
                int codePoint;
                if (!TextUtils.isEmpty(length)){
                    for (int j = 0; j < length.length(); j += Character.charCount(codePoint)) {
                        codePoint = length.codePointAt(j);
                        if ((codePoint >= 0xd800 && codePoint <= 0xdfff) || codePoint > 0x10ffff) {
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }


問題就可以解決了





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章