Android Https相关完全解析 当OkHttp遇到Https
http://blog.csdn.net/lmj623565791/article/details/48129405; 本文出自:【張鴻洋的博客】
1. 概述
其實(shí)這篇文章理論上不限于okhttp去訪問自簽名的網(wǎng)站,不過(guò)接上篇博文了,就叫這個(gè)了。首先要了解的事,okhttp默認(rèn)情況下是支持https協(xié)議的網(wǎng)站的,比如https://www.baidu.com,https://github.com/hongyangAndroid/okhttp-utils等,你可以直接通過(guò)okhttp請(qǐng)求試試。不過(guò)要注意的是,支持的https的網(wǎng)站基本都是CA機(jī)構(gòu)頒發(fā)的證書,默認(rèn)情況下是可以信任的。
當(dāng)然我們今天要說(shuō)的是自簽名的網(wǎng)站,什么叫自簽名呢?就是自己通過(guò)keytool去生成一個(gè)證書,然后使用,并不是CA機(jī)構(gòu)去頒發(fā)的。使用自簽名證書的網(wǎng)站,大家在使用瀏覽器訪問的時(shí)候,一般都是報(bào)風(fēng)險(xiǎn)警告,好在有個(gè)大名鼎鼎的網(wǎng)站就是這么干的,https://kyfw.12306.cn/otn/,點(diǎn)擊進(jìn)入12306的購(gòu)票頁(yè)面就能看到了。
如下界面:
大家可以嘗試拿okhttp訪問下:
OkHttpClientManager.getAsyn("https://kyfw.12306.cn/otn/", callack);1212會(huì)爆出如下錯(cuò)誤
javax.net.ssl.SSLHandshakeException:java.security.cert.CertPathValidatorException:Trust anchor for certification path not found.123123好了,本篇博文當(dāng)然不是去說(shuō)如何去訪問12306,而是以12306為例子來(lái)說(shuō)明如何去訪問自簽名證書的網(wǎng)站。因?yàn)椴糠珠_發(fā)者app與自己服務(wù)端交互的時(shí)候可能也會(huì)遇到自簽名證書的。甚至在開發(fā)安全級(jí)別很高的app時(shí),需要用到雙向證書的驗(yàn)證。
那么本篇博文的基本內(nèi)容包含:
- https一些相關(guān)的知識(shí)
- okhttp訪問自簽名https網(wǎng)站
- 如何構(gòu)建一個(gè)支持https的服務(wù)器(這里主要為了測(cè)試多個(gè)證書的時(shí)候,如何去加載)
- 如何進(jìn)行雙向證書驗(yàn)證
2. Https相關(guān)知識(shí)
關(guān)于特別理論的東西大家可以百度下自己去了解下,這里就簡(jiǎn)單說(shuō)一下,HTTPS相當(dāng)于HTTP的安全版本了,為什么安全呢?
因?yàn)樗贖TTP的之下加入了SSL (Secure Socket Layer),安全的基礎(chǔ)就靠這個(gè)SSL了。SSL位于TCP/IP和HTTP協(xié)議之間,那么它到底能干嘛呢?
它能夠:
- 認(rèn)證用戶和服務(wù)器,確保數(shù)據(jù)發(fā)送到正確的客戶機(jī)和服務(wù)器;(驗(yàn)證證書)
- 加密數(shù)據(jù)以防止數(shù)據(jù)中途被竊取;(加密)
- 維護(hù)數(shù)據(jù)的完整性,確保數(shù)據(jù)在傳輸過(guò)程中不被改變。(摘要算法)
以上3條來(lái)自百度
下面我們簡(jiǎn)單描述下HTTPS的工作原理,大家就能對(duì)應(yīng)的看到上面3條作用的身影了:
HTTPS在傳輸數(shù)據(jù)之前需要客戶端(瀏覽器)與服務(wù)端(網(wǎng)站)之間進(jìn)行一次握手,在握手過(guò)程中將確立雙方加密傳輸數(shù)據(jù)的密碼信息。握手過(guò)程的簡(jiǎn)單描述如下:
握手過(guò)程中如果有任何錯(cuò)誤,都會(huì)使加密連接斷開,從而阻止了隱私信息的傳輸。
ok,以上的流程不一定完全正確,基本就是這樣,當(dāng)然如果有明顯錯(cuò)誤歡迎指出。
根據(jù)上面的流程,我們可以看到服務(wù)器端會(huì)有一個(gè)證書,在交互過(guò)程中客戶端需要去驗(yàn)證證書的合法性,對(duì)于權(quán)威機(jī)構(gòu)頒發(fā)的證書當(dāng)然我們會(huì)直接認(rèn)為合法。對(duì)于自己造的證書,那么我們就需要去校驗(yàn)合法性了,也就是說(shuō)我們只需要讓OkhttpClient去信任這個(gè)證書就可以暢通的進(jìn)行通信了。
當(dāng)然,對(duì)于自簽名的網(wǎng)站的訪問,網(wǎng)上的部分的做法是直接設(shè)置信任所有的證書,對(duì)于這種做法肯定是有風(fēng)險(xiǎn)的,所以這里我們不去介紹了,有需要自己去查。
下面我們?nèi)タ紤],如何讓OkHttpClient去信任我們的證書,接下里的例子就是靠12306這個(gè)福利站點(diǎn)了。
首先導(dǎo)出12306的證書,這里12306提供了下載地址:12306證書點(diǎn)擊下載
下載完成,解壓拿到里面的srca.cer,一會(huì)需要使用。ps:即使沒有提供下載,也可以通過(guò)瀏覽器導(dǎo)出的,自行百度。
3. 代碼
3.1 訪問自簽名的網(wǎng)站
首先把我們下載的srca.cer放到assets文件夾下,其實(shí)你可以隨便放哪,反正能讀取到就行。
然后在我們的OkHttpClientManager里面添加如下的方法:
public void setCertificates(InputStream... certificates) {try{CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());keyStore.load(null);int index = 0;for (InputStream certificate : certificates){String certificateAlias = Integer.toString(index++);keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));try{if (certificate != null)certificate.close();} catch (IOException e){}}SSLContext sslContext = SSLContext.getInstance("TLS");TrustManagerFactory trustManagerFactory =TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagerFactory.init(keyStore);sslContext.init( null,trustManagerFactory.getTrustManagers(),new SecureRandom());mOkHttpClient.setSslSocketFactory(sslContext.getSocketFactory());} catch (Exception e){e.printStackTrace();}}為了代碼可讀性,我把異常捕獲的部分簡(jiǎn)化了,可以看到我們提供了一個(gè)方法傳入InputStream流,InputStream就對(duì)應(yīng)于我們證書的輸入流。
代碼內(nèi)部,我們:
- 構(gòu)造CertificateFactory對(duì)象,通過(guò)它的generateCertificate(is)方法得到Certificate。
- 然后講得到的Certificate放入到keyStore中。
- 接下來(lái)利用keyStore去初始化我們的TrustManagerFactory
- 由trustManagerFactory.getTrustManagers獲得TrustManager[]初始化我們的SSLContext
- 最后,設(shè)置我們mOkHttpClient.setSslSocketFactory即可。
這樣就完成了我們代碼的編寫,其實(shí)挺短的,當(dāng)客戶端進(jìn)行SSL連接時(shí),就可以根據(jù)我們?cè)O(shè)置的證書去決定是否新人服務(wù)端的證書。
記得在Application中進(jìn)行初始化:
public class MyApplication extends Application {@Overridepublic void onCreate(){super.onCreate();try{OkHttpClientManager.getInstance().setCertificates(getAssets().open("srca.cer"));} catch (IOException e){e.printStackTrace();}}然后嘗試以下代碼訪問12306的網(wǎng)站:
OkHttpClientManager.getAsyn("https://kyfw.12306.cn/otn/", new OkHttpClientManager.ResultCallback<String>() {@Overridepublic void onError(Request request, Exception e){e.printStackTrace();}@Overridepublic void onResponse(String u){mTv.setText(u);} });這樣即可訪問成功。完整代碼已經(jīng)更新至:https://github.com/hongyangAndroid/okhttp-utils,可以下載里面的sample進(jìn)行測(cè)試,里面包含12306的證書。
ok,到這就可以看到使用Okhttp可以很方便的應(yīng)對(duì)自簽名的網(wǎng)站的訪問,只需要拿到包含公鑰的證書即可。
3.2 使用字符串替代證書
下面繼續(xù),有些人可能覺得把證書copy到assets下還是覺得不舒服,其實(shí)我們還可以將證書中的內(nèi)容提取出來(lái),寫成字符串常量,這樣就不需要證書根據(jù)著app去打包了。
zhydeMacBook-Pro:temp zhy$ keytool -printcert -rfc -file srca.cer -----BEGIN CERTIFICATE----- MIICmjCCAgOgAwIBAgIIbyZr5/jKH6QwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ04xKTAn BgNVBAoTIFNpbm9yYWlsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRTUkNBMB4X DTA5MDUyNTA2NTYwMFoXDTI5MDUyMDA2NTYwMFowRzELMAkGA1UEBhMCQ04xKTAnBgNVBAoTIFNp bm9yYWlsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRTUkNBMIGfMA0GCSqGSIb3 DQEBAQUAA4GNADCBiQKBgQDMpbNeb34p0GvLkZ6t72/OOba4mX2K/eZRWFfnuk8e5jKDH+9BgCb2 9bSotqPqTbxXWPxIOz8EjyUO3bfR5pQ8ovNTOlks2rS5BdMhoi4sUjCKi5ELiqtyww/XgY5iFqv6 D4Pw9QvOUcdRVSbPWo1DwMmH75It6pk/rARIFHEjWwIDAQABo4GOMIGLMB8GA1UdIwQYMBaAFHle tne34lKDQ+3HUYhMY4UsAENYMAwGA1UdEwQFMAMBAf8wLgYDVR0fBCcwJTAjoCGgH4YdaHR0cDov LzE5Mi4xNjguOS4xNDkvY3JsMS5jcmwwCwYDVR0PBAQDAgH+MB0GA1UdDgQWBBR5XrZ3t+JSg0Pt x1GITGOFLABDWDANBgkqhkiG9w0BAQUFAAOBgQDGrAm2U/of1LbOnG2bnnQtgcVaBXiVJF8LKPaV 23XQ96HU8xfgSZMJS6U00WHAI7zp0q208RSUft9wDq9ee///VOhzR6Tebg9QfyPSohkBrhXQenvQ og555S+C3eJAAVeNCTeMS3N/M5hzBRJAoffn3qoYdAO1Q8bTguOi+2849A== -----END CERTIFICATE-----123456789101112131415123456789101112131415使用keytool命令,以rfc樣式輸出。keytool命令是JDK里面自帶的。
有了這個(gè)字符串以后,我們就不需要srca.cer這個(gè)文件了,直接編寫以下代碼:
public class MyApplication extends Application {private String CER_12306 = "-----BEGIN CERTIFICATE-----\n" +"MIICmjCCAgOgAwIBAgIIbyZr5/jKH6QwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ04xKTAn\n" +"BgNVBAoTIFNpbm9yYWlsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRTUkNBMB4X\n" +"DTA5MDUyNTA2NTYwMFoXDTI5MDUyMDA2NTYwMFowRzELMAkGA1UEBhMCQ04xKTAnBgNVBAoTIFNp\n" +"bm9yYWlsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRTUkNBMIGfMA0GCSqGSIb3\n" +"DQEBAQUAA4GNADCBiQKBgQDMpbNeb34p0GvLkZ6t72/OOba4mX2K/eZRWFfnuk8e5jKDH+9BgCb2\n" +"9bSotqPqTbxXWPxIOz8EjyUO3bfR5pQ8ovNTOlks2rS5BdMhoi4sUjCKi5ELiqtyww/XgY5iFqv6\n" +"D4Pw9QvOUcdRVSbPWo1DwMmH75It6pk/rARIFHEjWwIDAQABo4GOMIGLMB8GA1UdIwQYMBaAFHle\n" +"tne34lKDQ+3HUYhMY4UsAENYMAwGA1UdEwQFMAMBAf8wLgYDVR0fBCcwJTAjoCGgH4YdaHR0cDov\n" +"LzE5Mi4xNjguOS4xNDkvY3JsMS5jcmwwCwYDVR0PBAQDAgH+MB0GA1UdDgQWBBR5XrZ3t+JSg0Pt\n" +"x1GITGOFLABDWDANBgkqhkiG9w0BAQUFAAOBgQDGrAm2U/of1LbOnG2bnnQtgcVaBXiVJF8LKPaV\n" +"23XQ96HU8xfgSZMJS6U00WHAI7zp0q208RSUft9wDq9ee///VOhzR6Tebg9QfyPSohkBrhXQenvQ\n" +"og555S+C3eJAAVeNCTeMS3N/M5hzBRJAoffn3qoYdAO1Q8bTguOi+2849A==\n" +"-----END CERTIFICATE-----";@Overridepublic void onCreate(){super.onCreate();OkHttpClientManager.getInstance().setCertificates(new Buffer().writeUtf8(CER_12306).inputStream()); }注意Buffer是okio包下的,okhttp依賴okio。
ok,這樣就省去將cer文件一起打包進(jìn)入apk了。
接下來(lái)介紹,如何去生成證書以及在tomcat服務(wù)器下使用自簽名證書部署服務(wù)。如果大家沒這方面需要可以簡(jiǎn)單了解下。
4. tomcat下使用自簽名證書部署服務(wù)
首先自行下載個(gè)tomcat的壓縮包。
既然我們要支持https,那么肯定需要個(gè)證書,如何生成證書呢?使用keytool非常簡(jiǎn)單。
4.1 生成證書
zhydeMacBook-Pro:temp zhy$ keytool -genkey -alias zhy_server -keyalg RSA -keystore zhy_server.jks -validity 3600 -storepass 123456 您的名字與姓氏是什么?[Unknown]: zhang 您的組織單位名稱是什么?[Unknown]: zhang 您的組織名稱是什么?[Unknown]: zhang 您所在的城市或區(qū)域名稱是什么?[Unknown]: xian 您所在的省/市/自治區(qū)名稱是什么?[Unknown]: shanxi 該單位的雙字母國(guó)家/地區(qū)代碼是什么?[Unknown]: cn CN=zhang, OU=zhang, O=zhang, L=xian, ST=shanxi, C=cn是否正確?[否]: y輸入 <zhy_server> 的密鑰口令(如果和密鑰庫(kù)口令相同, 按回車):使用以上命令即可生成一個(gè)證書請(qǐng)求文件zhy_server.jks,注意密鑰庫(kù)口令為:123456.
接下來(lái)利用zhy_server.jks來(lái)簽發(fā)證書:
zhydeMacBook-Pro:temp zhy$ keytool -export -alias zhy_server-file zhy_server.cer-keystore zhy_server.jks-storepass 123456 12341234即可生成包含公鑰的證書zhy_server.cer。
4.2 配置Tomcat
找到tomcat/conf/sever.xml文件,并以文本形式打開。
在Service標(biāo)簽中,加入:
<Connector SSLEnabled="true" acceptCount="100" clientAuth="false"disableUploadTimeout="true" enableLookups="true" keystoreFile="" keystorePass="123456" maxSpareThreads="75"maxThreads="200" minSpareThreads="5" port="8443"protocol="org.apache.coyote.http11.Http11NioProtocol" scheme="https"secure="true" sslProtocol="TLS"/>注意keystoreFile的值為我們剛才生成的jks文件的路徑:/Users/zhy/ temp/zhy_server.jks(填寫你的路徑).keystorePass值為密鑰庫(kù)密碼:123456。
然后啟動(dòng)即可,對(duì)于命令行啟動(dòng),依賴環(huán)境變量JAVA_HOME;如果在MyEclispe等IDE下啟動(dòng)就比較隨意了。
啟動(dòng)成功以后,打開瀏覽器輸入url:https://localhost:8443/即可看到證書不可信任的警告了。選擇打死也要進(jìn)入,即可進(jìn)入tomcat默認(rèn)的主頁(yè):
如果你在此tomcat中部署了項(xiàng)目,即可按照如下url方式訪問:
https://192.168.1.103:8443/項(xiàng)目名/path,沒有部署也沒關(guān)系,直接拿默認(rèn)的主頁(yè)進(jìn)行測(cè)試了,拿它的html字符串。
對(duì)于訪問,還需要說(shuō)么,我們剛才已經(jīng)生成了zhy_server.cer證書。你可以選擇copy到assets,或者通過(guò)命令拿到內(nèi)部包含的字符串。我們這里選擇copy。
依然選擇在Application中設(shè)置信任證書:
public class MyApplication extends Application {private String CER_12306 = "省略...";@Overridepublic void onCreate(){super.onCreate();try{OkHttpClientManager.getInstance().setCertificates(new Buffer().writeUtf8(CER_12306).inputStream(),getAssets().open("zhy_server.cer"));} catch (IOException e){e.printStackTrace();}} }ok,這樣就能正常訪問你部署的https項(xiàng)目中的服務(wù)了,沒有部署項(xiàng)目的嘗試拿https://服務(wù)端ip:8443/測(cè)試即可。
注意:不要使用localhost,真機(jī)測(cè)試保證手機(jī)和服務(wù)器在同一局域網(wǎng)段內(nèi)。
ok,到此我們介紹完了如果搭建https服務(wù)和如何訪問,基本上可以應(yīng)付極大部分的需求了。當(dāng)然還是極少數(shù)的應(yīng)用需要雙向證書驗(yàn)證,比如銀行、金融類app,我們一起來(lái)了解下。
5. 雙向證書驗(yàn)證
首先對(duì)于雙向證書驗(yàn)證,也就是說(shuō),客戶端也會(huì)有個(gè)“kjs文件”,服務(wù)器那邊會(huì)同時(shí)有個(gè)“cer文件”與之對(duì)應(yīng)。
我們已經(jīng)生成了zhy_server.kjs和zhy_server.cer文件。
接下來(lái)按照生成證書的方式,再生成一對(duì)這樣的文件,我們命名為:zhy_client.kjs,zhy_client.cer.
6. 配置服務(wù)端
首先我們配置服務(wù)端:
服務(wù)端的配置比較簡(jiǎn)單,依然是剛才的Connector標(biāo)簽,不過(guò)需要添加些屬性。
<Connector 其他屬性與前面一致 clientAuth="true"truststoreFile="/Users/zhy/temp/zhy_client.cer"/> 12341234將clientAuth設(shè)置為true,并且多添加一個(gè)屬性truststoreFile,理論上值為我們的cer文件。這么加入以后,嘗試啟動(dòng)服務(wù)器,會(huì)發(fā)生錯(cuò)誤:Invalid keystore format。說(shuō)keystore的格式不合法。
我們需要對(duì)zhy_client.cer執(zhí)行以下步驟,將證書添加到kjs文件中。
keytool -import -alias zhy_client-file zhy_client.cer -keystore zhy_client_for_sever.jks1212接下里修改server.xml為:
<Connector 其他屬性與前面一致clientAuth="true"truststoreFile="/Users/zhy/temp/zhy_client_for_sever.jks"/> 12341234此時(shí)啟動(dòng)即可。
此時(shí)再拿瀏覽器已經(jīng)無(wú)法訪問到我們的服務(wù)了,會(huì)顯示基于證書的身份驗(yàn)證失敗。
我們將目標(biāo)來(lái)到客戶端,即我們的Android端,我們的Android端,如何設(shè)置kjs文件呢。
7. 配置app端
目前我們app端依靠的應(yīng)該是zhy_client.kjs。
ok,大家還記得,我們?cè)谥С謍ttps的時(shí)候調(diào)用了這么倆行代碼:
sslContext.init(null, trustManagerFactory.getTrustManagers(),new SecureRandom()); mOkHttpClient.setSslSocketFactory(sslContext.getSocketFactory());123123注意sslContext.init的第一個(gè)參數(shù)我們傳入的是null,第一個(gè)參數(shù)的類型實(shí)際上是KeyManager[] km,主要就用于管理我們客戶端的key。
于是代碼可以這么寫:
public void setCertificates(InputStream... certificates) {try{CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());keyStore.load(null);int index = 0;for (InputStream certificate : certificates){String certificateAlias = Integer.toString(index++);keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));try{if (certificate != null)certificate.close();} catch (IOException e){}}SSLContext sslContext = SSLContext.getInstance("TLS");TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());trustManagerFactory.init(keyStore);//初始化keystoreKeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());clientKeyStore.load(mContext.getAssets().open("zhy_client.jks"), "123456".toCharArray());KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());keyManagerFactory.init(clientKeyStore, "123456".toCharArray());sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());mOkHttpClient.setSslSocketFactory(sslContext.getSocketFactory());} catch (Exception e){e.printStackTrace();}}核心代碼其實(shí)就是:
//初始化keystore KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); clientKeyStore.load(mContext.getAssets().open("zhy_client.jks"), "123456".toCharArray());KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(clientKeyStore, "123456".toCharArray());sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); 123456789123456789然而此時(shí)啟動(dòng)會(huì)報(bào)錯(cuò):Java.io.IOException: Wrong version of key store.
為什么呢?
因?yàn)?#xff1a;Java平臺(tái)默認(rèn)識(shí)別jks格式的證書文件,但是android平臺(tái)只識(shí)別bks格式的證書文件。
這么就糾結(jié)了,我們需要將我們的jks文件轉(zhuǎn)化為bks文件,怎么轉(zhuǎn)化呢?
這里的方式可能比較多,大家可以百度,我推薦一種方式:
– 去Portecle下載Download portecle-1.9.zip (3.4 MB)。
解壓后,里面包含bcprov.jar文件,使用jave -jar bcprov.jar即可打開GUI界面。
按照上圖即可將zhy_client.jks轉(zhuǎn)化為zhy_client.bks。
然后將zhy_client.bks拷貝到assets目錄下,修改代碼為:
//初始化keystore KeyStore clientKeyStore = KeyStore.getInstance("BKS"); clientKeyStore.load(mContext.getAssets().open("zhy_client.bks"), "123456".toCharArray());KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(clientKeyStore, "123456".toCharArray());sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());1234567812345678再次運(yùn)行即可。然后就成功的做到了雙向的驗(yàn)證,關(guān)于雙向這塊大家了解下即可。
源碼都在https://github.com/hongyangAndroid/okhttp-utils之中。
ok,到此本篇博文就結(jié)束了,文章相當(dāng)?shù)拈L(zhǎng)~~ 關(guān)于okhttp在https協(xié)議下的使用,應(yīng)該沒什么問題。
ps:如果大家對(duì)okhttp-utils有任何建議,非常歡迎提出,最近根據(jù)大家的需求修改相當(dāng)頻繁
總結(jié)
以上是生活随笔為你收集整理的Android Https相关完全解析 当OkHttp遇到Https的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android OkHttp完全解析
- 下一篇: Retrofit2 完全解析 探索与ok