OkHTTP、Retrofit 中文亂碼解決方法

1. 亂碼出現(xiàn)的原因是什么?

出現(xiàn)亂碼的根本原因是客戶端、服務(wù)端兩端編碼格式不一致導(dǎo)致的。

2. 兩端的編碼格式一般是什么?

客戶端:多數(shù)情況下,客戶端的編碼格式是 UTF-8。
服務(wù)端:服務(wù)端會根據(jù)不同的請求方法使用不同的編碼格式。如:請求方法為 POST 時,編碼格式為 UTF-8;請求方法為 GET 時,編碼格式為 ISO8859-1。

3. 如何解決亂碼問題?

當(dāng)請求方法為 POST 時,客戶端和服務(wù)端兩邊的編碼格式一致,所以不存在亂碼問題。因此此處著重看下如何解決當(dāng)請求方法為 GET 時的亂碼問題。

解決方法倒也簡單,只不過需要客戶端和服務(wù)端配合:

3.1 客戶端需要做什么?

在向 URL 添加參數(shù)之前,先對目標(biāo)參數(shù)進行兩次 encode,如 UTF-8:

String username = "xxx";
username = URLEncoder.encode(username,"UTF-8");
username = URLEncoder.encode(username,"UTF-8");
String url = xxxxx.xxx + "?username=" + username + "&password=" + password;

3.2 服務(wù)端需要做什么?

服務(wù)器在收到數(shù)據(jù)之后,只需將數(shù)據(jù)進行一次跟客戶端編碼格式一樣的 decode,如 UTF-8:

String username = URLDecoder.decode(username, "UTF-8")

這樣處理之后,兩邊就不會再出現(xiàn)亂碼了。

4. 為什么對 URL 中的參數(shù)進行兩次 encode 之后,就可以解決亂碼問題了?

4.1 原理解析

通過上面的分析可知,亂碼產(chǎn)生的主要原因是客戶端、服務(wù)器兩邊編碼不一致造成的,即發(fā)送 GET 請求時,客戶端使用的是 UTF-8 編碼格式對 URL 中的參數(shù)進行編碼,而服務(wù)器在接收數(shù)據(jù)的時候,使用的是 ISO8859-1(解析 POST 請求時,服務(wù)器使用的編碼格式是 UTF-8 編碼格式)編碼格式對 URL 中的參數(shù)進行解碼。

ISO8859-1 跟 ASCII 碼一樣,都是單字節(jié)編碼,ISO8859-1 是從 ASCII 擴展而來的。ISO8859-1 將 ASCII 一個字節(jié)中剩余的最后一位用了起來,也就是說,它比 ASCII 多了 128 個字符。另外,因為 ISO8859-1 是從 ASCII 擴展而來的,所以,ISO8859-1 兼容 ASCII。

原數(shù)據(jù):

極速

客戶端第一次編碼,URLDecoder.decode(username, "UTF-8") 編碼之后:

%E6%9E%81%E9%80%9F

客戶端第二次編碼,URLDecoder.decode(username, "UTF-8") 編碼之后:

%25E6%259E%2581%25E9%2580%259F

客戶端發(fā)出的 URL:

http://192.168.31.148:8080/OkHttpServer/login?username=%25E6%259E%2581%25E9%2580%259F&password=123456

服務(wù)器接收的 URL:

http://192.168.31.148:8080/OkHttpServer/login?username=%25E6%259E%2581%25E9%2580%259F&password=123456

服務(wù)器第一次解碼,服務(wù)器接收到 GET 請求之后,默認會用 ISO8859-1 編碼格式解碼,解碼之后得到:

http://192.168.31.148:8080/OkHttpServer/login?username=%E6%9E%81%E9%80%9F&password=123456

需要注意的是,服務(wù)器用 ISO8859-1 編碼格式解碼 URL 中的參數(shù)是自動完成的。
因為客戶端第一次用 URLDecoder.decode(username, "UTF-8") 編碼 URL 中參數(shù)之后,得到的是 ASCII 碼,且 UTF-8 和 ISO8859-1 對 ASCII 的編碼結(jié)果是一致的,所以,客戶端第二次用 URLDecoder.decode(username, "UTF-8") 之后的結(jié)果可以直接用 ISO8859-1 編碼格式解碼。
由于服務(wù)器解碼之后的 URL 中的參數(shù)是用 UTF-8 編碼格式編碼的,所以,此時需要服務(wù)器再用 UTF-8 編碼格式解碼一次。

服務(wù)器第二次解碼,服務(wù)器用 UTF-8 編碼格式解碼之后得到:

http://192.168.31.148:8080/OkHttpServer/login?username=極速&password=123456

4.2 實際應(yīng)用

如果客戶端程序員沒有顯式用 UTF-8 編碼格式編碼 URL 中的參數(shù),服務(wù)端要如何處理才能獲取到原數(shù)據(jù)?

首先,分析下如果客戶端沒有用 UTF-8 編碼格式編碼 URL 中的參數(shù),程序是如何執(zhí)行的:

網(wǎng)絡(luò)請求框架會對 URL 中的參數(shù)進行一次 UTF-8 編碼:

URLDecoder.encode(username, "UTF-8")

服務(wù)器會對 URL 中的參數(shù)進行一次 ISO8859-1 編碼:

URLDecoder.decode(username, "ISO8859-1")

明白了執(zhí)行流程之后,如何解決自然也就顯而易見了:
先轉(zhuǎn)回 ISO8859-1 解碼(decode)之前的結(jié)果,再轉(zhuǎn)會 UTF-8 編碼(encode)之前的結(jié)果。

具體操作步驟:

//1. 先轉(zhuǎn)回 ISO8859-1 解碼(decode)之前的結(jié)果
String temp = URLDecoder.encode(username, "ISO8859-1");
//2. 再轉(zhuǎn)會 UTF-8 編碼(encode)之前的結(jié)果
temp = URLDecoder.decode(username, "UTF-8")

4.3 為什么 URL 中的參數(shù)經(jīng) UTF-8 編碼格式編碼之后不能通過 ISO8859-1 編碼格式直接解碼呢?

因為 URL 中的參數(shù)經(jīng) UTF-8 編碼格式編碼之后得到的結(jié)果在 ISO8859-1 字符集可能一樣也可能根本表示不了,這也是為什么 ASCII 碼經(jīng) UTF-8 編碼格式編碼之后的結(jié)果可以用 ISO8859-1 編碼格式解碼。如,在 Unicode 字符集中,第 20013 個字符是“中”,而在 ISO8859-1 字符集中,一共才有 256 個字符。字符“中”經(jīng) UTF-8 編碼之后的結(jié)果再經(jīng) ISO8859-1 解碼,無論如何也得不到正確答案的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容