通過Android SDK接入流程這篇文檔,您已經瞭解了Android SDK匯入、配置、解析IP、應用到網路程式庫和接入驗證的完整流程,本文主要介紹OkHttp接入HTTPDNS的具體方案。
1. 背景說明
如果您在Android端使用的網路架構是OkHttp,通過調用OkHttp提供的自訂DNS服務介面,可以簡潔優雅地使用HTTPDNS的服務。
OkHttp是一個處理網路請求的開源專案,是Android端最火熱的輕量級架構,由移動支付Square公司貢獻用於替代HttpUrlConnection和Apache HttpClient。隨著OkHttp的不斷成熟,越來越多的Android開發人員使用OkHttp作為網路架構。
OkHttp預設使用系統DNS服務InetAddress進行網域名稱解析,同時也暴露了自訂DNS服務的介面,通過該介面我們可以優雅地使用HTTPDNS。
本實踐對於Retrofit+OkHttp同樣適用,將配置好的OkHttpClient作為Retrofit.Builder::client(OkHttpClient)參數傳入即可。
2. 自訂DNS介面
OkHttp暴露了一個DNS介面,通過實現該介面,我們可以自訂DNS服務:
class OkHttpDns constructor(context: Context): Dns {
private var mContext: Context
init {
mContext = context
}
@Throws(UnknownHostException::class)
override fun lookup(host: String): List<InetAddress> {
val httpdnsResult: HTTPDNSResult = HttpDns.getService(accountID)
.getHttpDnsResultForHostSyncNonBlocking(host, RequestIpType.auto)
val inetAddresses: MutableList<InetAddress> = ArrayList()
var address: InetAddress
try {
if (httpdnsResult.ips != null) {
//處理IPv4地址
for (ipv4 in httpdnsResult.ips) {
address = InetAddress.getByName(ipv4)
inetAddresses.add(address)
}
}
if (httpdnsResult.ipv6s != null) {
//處理IPv6地址
for (ipv6 in httpdnsResult.ipv6s) {
address = InetAddress.getByName(ipv6)
inetAddresses.add(address)
}
}
} catch (e: UnknownHostException) {
}
return if (!inetAddresses.isEmpty()) {
inetAddresses
} else Dns.SYSTEM.lookup(host)
}
}public class OkHttpDns implements Dns {
private Context mContext;
public OkHttpDns(Context context) {
mContext = context;
}
@Override
public List<InetAddress> lookup(String host) throws UnknownHostException {
HTTPDNSResult httpdnsResult = HttpDns.getService(accountID).getHttpDnsResultForHostSync(host, RequestIpType.both);
List<InetAddress> inetAddresses = new ArrayList<>();
InetAddress address;
try {
if (httpdnsResult.getIps() != null) {
//處理IPv4地址
for (String ipv4 : httpdnsResult.getIps()) {
address = InetAddress.getByName(ipv4);
inetAddresses.add(address);
}
}
if (httpdnsResult.getIpv6s() != null) {
//處理IPv6地址
for (String ipv6 : httpdnsResult.getIpv6s()) {
address = InetAddress.getByName(ipv6);
inetAddresses.add(address);
}
}
} catch (UnknownHostException e) {
}
if (!inetAddresses.isEmpty()) {
return inetAddresses;
}
return okhttp3.Dns.SYSTEM.lookup(host);
}
}由於okhttp內部有重試機制,重試時可能會選擇不同的IP建立串連,所以在Dns介面的lookup()方法中,可以返回包含多個IP的List<InetAddress>,可以有效避免返回單一IP,而這個IP不可用導致該次請求完全失敗的情況。
3. 配置OkHttpClient
建立OkHttpClient對象,傳入OkHttpDns對象代替預設DNS服務:
private fun initOkHttp(context: Context) {
val client = OkHttpClient.Builder()
//okhttp其他初始化配置請根據需求進行配置
.dns(OkHttpDns(context))
.build()
}private void initOkHttp(Context context) {
OkHttpClient client = new OkHttpClient.Builder()
//okhttp其他初始化配置請根據需求進行配置
.dns(new OkHttpDns(context))
.build();
}以上就是OkHttp+HttpDns實現的全部代碼。
4. 驗證
接入完成後,請參考驗證網路程式庫驗證成功文檔,通過劫持類比或錯誤注射測試等方式,驗證整合是否成功。
5. 總結
OkHttp+HttpDns有以下兩個主要優勢:
實現簡單,只需通過實現DNS介面即可接入HttpDns服務。
通用性強,該方案在HTTPS,SNI以及設定Cookie等情境均適用。規避了認證校正,網域名稱檢查等環節