全部產品
Search
文件中心

HTTPDNS:Android端HTTPDNS+OkHttp接入指南

更新時間:Jul 13, 2024

重要

當前最佳實務文檔只針對結合使用時,如何使用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)參數傳入即可。