Android端HTTPDNS+阿里云OSS SDK最佳实践

本文档介绍在Android客户端上使用阿里云OSS SDK接入HTTPDNS的方案。

1. 背景说明

阿里云对象存储OSS(Object Storage Service)是一款通用的云存储服务,可让开发者在各类应用中便捷地存储和访问用户头像、图片、音视频等文件。在Android端,可以通过集成OSS Android SDK来完成。

为了提升文件传输的稳定性和速度,可以将HTTPDNS SDKOSS Android SDK进行结合,对OSS域名进行解析。这可以有效实现域名防劫持和DNS解析加速,从而优化应用访问OSS的网络体验。

2. 基于OkHttp接入HTTPDNS SDK

实现OkHttp的自定义DNS接口,接入HTTPDNS,具体参考AndroidHTTPDNS+OkHttp最佳实践

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)
            .getHttpDnsResultForHostSync(host, RequestIpType.both)
        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;
    }
    @Overridepublic 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);
    }
}

3. 接入OSS SDK并使用HTTPDNS

创建OKHttpClient实例,并配置到OSS代替默认DNS服务。OSS SDK的接入和使用,可以参考OSS Android SDK

// 创建OSS客户端配置
val conf = ClientConfiguration()
conf.connectionTimeout = 15 * 1000 // 连接超时,默认15秒。
conf.socketTimeout = 15 * 1000 // socket超时,默认15秒。
conf.maxConcurrentRequest = 5 // 最大并发请求书,默认5个。
conf.maxErrorRetry = 2 // 失败后最大重试次数,默认2次。

val builder = OkHttpClient.Builder()
    .dns(OkHttpDns(applicationContext))

// 如果设置了自定义的okHttpClient,ClientConfiguration的部分设置将会失效。您需要将其手动设置到builder上。
if (conf != null) {
    val dispatcher = Dispatcher()
    dispatcher.maxRequests = conf.maxConcurrentRequest

    builder.connectTimeout(conf.connectionTimeout.toLong(), TimeUnit.MILLISECONDS)
        .readTimeout(conf.socketTimeout.toLong(), TimeUnit.MILLISECONDS)
        .writeTimeout(conf.socketTimeout.toLong(), TimeUnit.MILLISECONDS)
        .followRedirects(conf.isFollowRedirectsEnable)
        .followSslRedirects(conf.isFollowRedirectsEnable)
        .dispatcher(dispatcher)

    if (conf.proxyHost != null && conf.proxyPort != 0) {
        builder.proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress(conf.proxyHost, conf.proxyPort)))
    }
}

// 仅Android OSS SDK 2.9.12或以上版本支持使用conf.setOkHttpClient()方法。
conf.okHttpClient = builder.build()

val oss = OSSClient(applicationContext, endpoint, credentialProvider, conf)
// 创建OSS客户端配置
ClientConfiguration conf = new ClientConfiguration();
conf.setConnectionTimeout(15 * 1000); // 连接超时,默认15秒。
conf.setSocketTimeout(15 * 1000); // socket超时,默认15秒。
conf.setMaxConcurrentRequest(5); // 最大并发请求书,默认5个。
conf.setMaxErrorRetry(2); // 失败后最大重试次数,默认2次。

OkHttpClient.Builder builder = new OkHttpClient.Builder()
        .dns(new OkHttpDns(getApplicationContext()));
        
// 如果设置了自定义的okHttpClient,ClientConfiguration的部分设置将会失效。您需要将其手动设置到builder上。
if (conf != null) {
    Dispatcher dispatcher = new Dispatcher();
    dispatcher.setMaxRequests(conf.getMaxConcurrentRequest());

    builder.connectTimeout(conf.getConnectionTimeout(), TimeUnit.MILLISECONDS)
            .readTimeout(conf.getSocketTimeout(), TimeUnit.MILLISECONDS)
            .writeTimeout(conf.getSocketTimeout(), TimeUnit.MILLISECONDS)
            .followRedirects(conf.isFollowRedirectsEnable())
            .followSslRedirects(conf.isFollowRedirectsEnable())
            .dispatcher(dispatcher);

    if (conf.getProxyHost() != null && conf.getProxyPort() != 0) {
        builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(conf.getProxyHost(), conf.getProxyPort())));
    }
}

// 仅Android OSS SDK 2.9.12或以上版本支持使用conf.setOkHttpClient()方法。
conf.setOkHttpClient(builder.build());

OSS oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider, conf);
重要

HTTPDNS SDK初始化设置需要在OSS SDK初始化设置之前完成。

4. 总结

通过将HTTPDNSOSS SDK结合,可以有效防止DNS劫持并加速文件传输,提升访问的稳定性和安全性。

实现的核心是为OSS配置一个带有HTTPDNS解析逻辑的自定义OkHttpClient

关键注意点:

  • 自定义OkHttpClient后,必须将ClientConfiguration中的超时、并发数等配置手动设置到OkHttpClient.Builder上,否则原配置会失效。

  • 请确保OSS SDK版本不低于2.9.12,并先初始化HTTPDNS,再初始化OSS。