當前最佳實務文檔只針對結合使用時,如何使用HTTPDNS解析出的IP,關於HTTPDNS本身的解析服務,請先查看Android SDK開發指南。
背景資訊
Android端HTTPS(含SNI)業務情境:IP直連方案一文介紹了利用HTTPDNS解析獲得IP後進行IP直連的通用方法。但是如果您在Android端使用的網路架構是OkHttp,通過調用OkHttp提供的自訂DNS服務介面,可以更為優雅地使用HTTPDNS的服務。
OkHttp是一個處理網路請求的開源專案,是Android端最火熱的輕量級架構,由移動支付Square公司貢獻用於替代HttpUrlConnection和Apache HttpClient。隨著OkHttp的不斷成熟,越來越多的Android開發人員使用OkHttp作為網路架構。
OkHttp預設使用系統DNS服務InetAddress
進行網域名稱解析,但同時也暴露了自訂DNS服務的介面,通過該介面我們可以優雅地使用HTTPDNS。
自訂DNS介面
OkHttp暴露了一個DNS介面,通過實現該介面,我們可以自訂DNS服務:
public class OkHttpDns implements Dns {
private static final Dns SYSTEM = Dns.SYSTEM;
HttpDnsService httpdns;//httpdns 解析服務
private static OkHttpDns instance = null;
private OkHttpDns(Context context) {
this.httpdns = HttpDns.getService(context, "account id");
}
public static OkHttpDns getInstance(Context context) {
if(instance == null) {
instance = new OkHttpDns(context);
}
return instance;
}
@Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
//通過非同步解析介面擷取ip
String ip = httpdns.getIpByHostAsync(hostname);
if(ip != null) {
//如果ip不為null,直接使用該ip進行網路請求
List<InetAddress> inetAddresses = Arrays.asList(InetAddress.getAllByName(ip));
Log.e("OkHttpDns", "inetAddresses:" + inetAddresses);
return inetAddresses;
}
//如果返回null,走系統DNS服務解析網域名稱
return Dns.SYSTEM.lookup(hostname);
}
}
Dns
介面附加說明:
Dns
介面返回的IP,在okhttp進行網路請求時,受OkHttpClient的connectTimeout()
影響,具體為:
1、舉例:如果OkHttpClient的connectTimeout()
設定為5s,即嘗試串連的逾時時間為5s,如果發生連線逾時,同一IP會嘗試進行兩次串連,即同一IP的逾時總時間是10s。
2、Dns
介面,lookup()
方法中,return的List<InetAddress>
如果為多個IP,會順序使用這些IP進行串連。
如果第一個IP建連成功,則直接完成請求。
如果第一個IP不可用,結合上述第一點,會使用第一個IP嘗試串連兩次,即兩次逾時時間後,順序使用下一個IP嘗試串連。以此類推。
綜合兩點所述,在Dns
介面,lookup()
方法中,可以return包含多個IP的List<InetAddress>
,可以有效避免返回單一IP,而這個IP不可用導致該次請求完全失敗的情境。需要注意逾時時間的設定,可以稍微給短一點。
建立OkHttpClient
建立OkHttpClient對象,傳入OkHttpDns對象代替預設DNS服務:
private void okhttpDnsRequest() {
OkHttpClient client = new OkHttpClient.Builder()
.dns(OkHttpDns.getInstance(getApplicationContext()))
.build();
Request request = new Request.Builder()
.url("http://www.aliyun.com")
.build();
Response response = null;
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
DataInputStream dis = new DataInputStream(response.body().byteStream());
int len;
byte[] buff = new byte[4096];
StringBuilder result = new StringBuilder();
while ((len = dis.read(buff)) != -1) {
result.append(new String(buff, 0, len));
}
Log.d("OkHttpDns", "Response: " + result.toString());
}
});
}
以上就是OkHttp+HttpDns實現的全部代碼。
總結
相比於通用方案,OkHttp+HttpDns有以下兩個主要優勢:
實現簡單,只需通過實現DNS介面即可接入HttpDns服務。
通用性強,該方案在HTTPS,SNI以及設定Cookie等情境均適用。規避了認證校正,網域名稱檢查等環節
該實踐對於Retrofit+OkHttp同樣適用,將配置好的OkHttpClient作為Retrofit.Builder::client(OkHttpClient)
參數傳入即可。