前言
本章节介绍HTTPDNS Android SDK的接入方法。
推荐使用Gradle管理依赖的Android Studio项目。
支持Android 4.4(API Level 19)及以上版本。
targetSdkVersion支持到Android 14(API = 34)。
支持arm64-v8a/armeabi-v7a/x86/x86_64架构。
SDK已开源,如有特殊需求,可以自行更改源码进行使用,请参见httpdns-android-sdk。
准备工作
开通服务与创建项目。具体操作请参见产品使用流程。
第一步:将SDK添加到您的应用
我们提供了Maven依赖和本地依赖两种集成方式,方便您根据需要将SDK添加到您的应用中。
建议开发者采用Maven依赖方式进行集成,配置简单,不容易出问题,后续更新方便。
1 Maven依赖方式(推荐)
1.1 配置Maven仓库
下面分别介绍Gradle 7.0及以上推荐的dependencyResolutionManagement
配置方式和Gradle 7.0以下推荐的allprojects
配置方式。
1.1.1 dependencyResolutionManagement方式
在您的根级(项目级)Gradle 文件(<project>/settings.gradle
)中,在dependencyResolutionManagement
的repositories
中添加Maven仓库地址。
dependencyResolutionManagement {
repositories {
maven {
url 'https://maven.aliyun.com/nexus/content/repositories/releases/'
}
}
}
1.1.2 allprojects方式
在您的根级(项目级)Gradle 文件(<project>/build.gradle
)中,在allprojects
的repositories
中添加Maven仓库地址。
allprojects {
repositories {
maven {
url 'https://maven.aliyun.com/nexus/content/repositories/releases/'
}
}
}
1.2 添加SDK依赖
在您的模块(应用级)Gradle 文件(通常是<project>/<app-module>/build.gradle)中,在dependencies
中添加SDK依赖。
dependencies {
implementation 'com.aliyun.ams:alicloud-android-httpdns:${httpdnsVersion}'
}
httpdnsVersion
请从Android SDK发布说明中获取。
2 本地依赖方式
2.1 下载SDK
从EMAS SDK列表选择HTTPDNS进行下载,将SDK包内所有文件拷贝至您的模块(应用级)的<project>/<app-module>/libs
目录下。
2.2 添加SDK依赖
2.2.1 配置本地SDK目录
在您的模块(应用级)Gradle 文件(通常是<project>/<app-module>/build.gradle)中,添加本地SDK文件目录地址。
repositories {
flatDir {
dirs 'libs'
}
}
2.2.2 添加SDK依赖
在您的模块(应用级)Gradle 文件(通常是<project>/<app-module>/build.gradle)中,在dependencies
中添加SDK依赖。
dependencies {
implementation (name: 'alicloud-android-httpdns-${httpdnsVersion}', ext: 'aar')
implementation (name: 'alicloud-android-logger-${loggerVersion}', ext: 'aar')
implementation (name: 'alicloud-android-crashdefend-${crashDefendVersion}', ext: 'jar')
implementation (name: 'alicloud-android-ipdetector-${ipdetectorVersion}', ext: 'aar')
implementation 'commons-codec:commons-codec:1.15'
}
示例依赖中的SDK版本号请根据下载产物的文件名中的版本号为准。
如果编译过程中报类缺失,请确认dependencies下是否已经有
implementation fileTree(dir: 'libs', include: ['*.jar'])
第二步:配置使用SDK
1 配置HTTPDNS
//初始化配置,调用即可,不必处理返回值。
InitConfig config = InitConfig.Builder()
/*
(可选)设置应用安装后首次域名解析访问的HTTPDNS服务集群,后续解析请求会依据客户端网络就近访问,若业务不希望首次访问到默认集群,可在此显式选择目标区域(如 Region.SG、Region.HK),中国站默认中国大陆,国际站默认新加坡集群。
*/
.setRegion(Region.DEFAULT)
/*
(必填)传入应用 Context,用于网络变化事件感知等,通常传 ApplicationContext 即可。
*/
.setContext(context)
/*
(可选)配置与服务端交互的加签秘钥,可防止请求被篡改,默认不开启,建议开启。
*/
.setSecretKey(secretKey)
/*
(可选)配置与服务端交互的内容加密密钥,开启后所有请求与响应都会加密传输,安全性更高,但计费方式会随之调整,若已启用软件自定义解析且业务参数敏感,建议开启,其他场景可以不开启。
*/
.setAesSecretKey(aesSecretKey)
/*
(可选) 配置SDK与服务端交互的传输层协议,开启后可获得更高的链路安全性,同时需关注 HTTP 与 HTTPS 在计费逻辑上的差异,默认HTTP协议,建议开启。
*/
.setEnableHttps(enableHttps)
/*
(可选)设置域名解析超时时间,毫秒,最大5000ms,默认2000ms。
*/
.setTimeoutMillis(2 * 1000)
/*
(可选)配置是否启用本地缓存,用于优化启动后的域名解析环节耗时,进而提升首屏加载速度,默认不启用,建议开启并设置缓存时间为1天。
*/
.setEnableCacheIp(true, 24 * 60 * 60 * 1000)
/*
(可选)设置对解析结果的IP列表进行嗅探排序,设置嗅探的域名和探测端口的列表,对延迟敏感的请求,建议配置
*/
.setIPRankingList(ipRankingItemJson.toIPRankingList())
/*
(可选) 配置接口来自定义缓存的ttl时间,以改变的权威DNS默认缓存TTL
*/
.configCacheTtlChanger(ttlChanger)
/*
(可选) 部分域名的IP是相对稳定的,这部分域名的缓存在网络变化等场景下可以不用频繁刷新,可以通过该接口显式告诉SDK
*/
.configHostWithFixedIp(hostListWithFixedIp)
/*
(可选)配置不使用HTTPDNS解析的域名筛选条件,对于列表中的域名,HTTPDNS立即返回空
*/
.setNotUseHttpDnsFilter(notUseHttpDnsFilter)
.build()
HttpDns.init(accountID, config);
//初始化配置,调用即可,不必处理返回值。
InitConfig config = new InitConfig.Builder()
/*
(可选)设置应用安装后首次域名解析访问的HTTPDNS服务集群,后续解析请求会依据客户端网络就近访问,若业务不希望首次访问到默认集群,可在此显式选择目标区域(如 Region.SG、Region.HK),中国站默认中国大陆,国际站默认新加坡集群。
*/
.setRegion(Region.DEFAULT)
/*
(必填)传入应用 Context,用于网络变化事件感知等,通常传 ApplicationContext 即可。
*/
.setContext(context)
/*
(可选)配置与服务端交互的加签秘钥,可防止请求被篡改,默认不开启,建议开启。
*/
.setSecretKey(secretKey)
/*
(可选)配置与服务端交互的内容加密密钥,开启后所有请求与响应都会加密传输,安全性更高,但计费方式会随之调整,若已启用软件自定义解析且业务参数敏感,默认开启,建议开启。
*/
.setAesSecretKey(aesSecretKey)
/*
(可选) 配置SDK与服务端交互的传输层协议,开启后可获得更高的链路安全性,同时需关注 HTTP 与 HTTPS 在计费逻辑上的差异,默认HTTP协议,建议开启。
*/
.setEnableHttps(enableHttps)
/*
(可选)设置域名解析超时时间,毫秒,最大5000ms,默认2000ms。
*/
.setTimeoutMillis(2 * 1000)
/*
(可选)配置是否启用本地缓存,用于优化启动后的域名解析环节耗时,进而提升首屏加载速度,默认不启用,建议开启并设置缓存时间为1天。
*/
.setEnableCacheIp(true, 24 * 60 * 60 * 1000)
/*
(可选)设置对解析结果的IP列表进行嗅探排序,设置嗅探的域名和探测端口的列表,对延迟敏感的业务,建议配置
*/
.setIPRankingList(ipRankingItemJson.toIPRankingList())
/*
(可选) 配置接口来自定义缓存的ttl时间,以改变的权威DNS默认缓存TTL
*/
.configCacheTtlChanger(ttlChanger)
/*
(可选) 部分域名的IP是相对稳定的,这部分域名的缓存在网络变化等场景下可以不用频繁刷新,可以通过该接口显式告诉SDK
*/
.configHostWithFixedIp(hostListWithFixedIp)
/*
(可选)配置不使用HTTPDNS解析的域名筛选条件,对于列表中的域名,HTTPDNS立即返回空
*/
.setNotUseHttpDnsFilter(notUseHttpDnsFilter)
.build()
HttpDns.init(accountID, config);
如果初始化的时候
setEnableHttps
没有设置成true
,需要在应用级的AndroidManifest.xml
文件下的application
节点下添加配置android:usesCleartextTraffic="true"
,否则域名解析请求在高版本(targetSdkVersion 27及以上)系统上会失败。为避免在日志中泄露参数accountID/secretKey或App运行过程中产生的数据,建议线上版本是否允许HTTPDNS打印Log。
由于所有用户使用统一的SDK接入,在接入过程中需要在代码中设置accountID/secretKey/aesSecretKey参数,而此类参数与计量计费密切相关,为防止恶意反编译获取参数造成信息泄露,建议您开启混淆,并进行App加固后再发布上线。
具体配置接口信息请查看配置接口。
2 获取服务实例
HTTPDNS Android SDK以全局service实例的方式提供域名解析服务,您可以通过以下方式获取实例。
val httpdns = HttpDns.getService(accountID)
HttpDnsService httpdns = HttpDns.getService(accountID);
3 进行域名解析
HTTPDNS提供了多种域名解析方式,包括预解析、同步解析、异步解析和同步非阻塞解析。下面以同步非阻塞解析接口作为例子。
val httpDnsResult = dnsService?.getHttpDnsResultForHostSyncNonBlocking("www.aliyun.com", RequestIpType.auto)
HTTPDNSResult httpDnsResult = httpdns.getHttpDnsResultForHostSyncNonBlocking("www.aliyun.com", RequestIpType.auto);
getHttpDnsResultForHostSyncNonBlocking 该接口仅查询缓存,返回缓存查询的解析结果。若缓存中没有解析结果或者缓存中的解析结果已经TTL过期,则会在工作线程中进行域名解析,解析成功后更新缓存,供下次调用域名解析使用。
配合预解析setPreResolveHosts、持久化缓存setEnableCacheIp和允许过期接口setEnableExpiredIp,能使得绝大多数的解析请求命中本地缓存,实现0ms解析延迟。
若您的业务强依赖HTTPDNS解析的结果,比如使用自定义解析、Local DNS劫持严重,需要使用getHttpDnsResultForHostSync或getHttpDnsResultForHostAsync。
4 使用域名解析结果
上一步解析成功后,可以获得域名解析结果,数据结构请查看HTTPDNSResult。
此处以okhttp网络库的解析过程为例,示例代码如下:
object : Dns {
@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
// 重要:配置降级LocalDNS
} else Dns.SYSTEM.lookup(host)
}
}
new Dns() {
@Override
public List<InetAddress> lookup(String host) throws UnknownHostException {
HTTPDNSResult httpdnsResult = HttpDns.getService(accountID).getHttpDnsResultForHostSync(host, RequestIpType.auto);
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 Dns.SYSTEM.lookup(host);
}
};
5 混淆配置
如果您的项目做了代码混淆,请保留以下混淆配置。
-keep class com.alibaba.sdk.android.**{*;}
第三步:接入验证
接入验证包括两个环节:1)通过SDK获取了域名对应的IP地址;2)网络库使用了HTTPDNS SDK返回的IP,并且访问业务服务成功。
1 验证IP获取成功
您可以直接打印SDK返回的结果,也可以按照是否允许HTTPDNS打印Log打开HTTPDNS Sdk的日志。当您从日志里看到IP列表,说明解析成功。
# 发起同步请求
D sync request host www.aliyun.com with type both extras : null cacheKey null
# 缓存查询结果
D host www.aliyun.com result in cache is null
# 触发云端解析
I sync start request for www.aliyun.com both
D ip detector type is 3
D ipdetector type is both
D start resolve ip request for www.aliyun.com both
# 解析超时时长
D the httpDnsConfig timeout is: 2000
D final timeout is: 2000
D wait for request finish
# 发起解析请求
D request url http://xx.xx.xx.xx:80/xxxx/sign_d?host=www.aliyun.com&sdk=android_2.4.0&query=4,6&sid=CaZk1vTyI3hy&s=b4a34694b7215b4cd6a10376b3425a8e&t=1715772172
# 解析成功
D request success {"ipsv6":[],"host":"www.aliyun.com","ips":[],"ttl":60,"origin_ttl":60}
# 更新缓存
D save host www.aliyun.com for type v4 ttl is 60
D save host www.aliyun.com for type v6 ttl is 60
D sync resolve time is: 224
# 返回解析结果
I request host www.aliyun.com for both and return host:www.aliyun.com, ips:[], ipv6s:[], extras:{}, expired:false, fromDB:false after request
2 验证网络库验证成功
网络库接入HTTPDNS成功的标准是DNS解析是否绕过了LocalDNS,使用了HTTPDNS。您可以通过以下两种方式进行验证:
劫持模拟:通过将手机WIFI网络的 DNS 服务器设置为一个无效地址,观察业务请求依然能够正常发起,则接入成功。
错误注入测试:利用 EMAS HTTPDNS 的自定义解析能力添加自定义解析记录,把目标域名解析到一个无效的 IP,观察业务请求失败,则接入成功。
注意事项
升级SDK后编译失败
如果您升级SDK版本后,出现编译失败的情况,有可能是因为SDK的API有调整,请查看Android SDK API使用替换的新接口。
务必编写降级代码
降级代码指的是当HTTPDNS无法获取期望结果时,需要降级使用Local DNS去完成域名解析。
记录从HTTPDNS获取的IP及sessionId
我们提供了用于解析问题排查的解决方案,需要您将从HTTPDNS获取的IP及sessionId记录到日志中,详情请参见如何使用“会话追踪方案”排查解析异常。