前言
本章节介绍HTTPDNS Android SDK的接入方法。
推荐使用Gradle管理依赖的Android Studio项目。
支持Android 4.0及以上版本。
targetSdkVersion支持到Android 14(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 (name: 'alicloud-android-utdid-${utdidVersion}', ext: 'jar')
}
示例依赖中的SDK版本号请根据下载产物的文件名中的版本号为准。
如果编译报类冲突,请确认dependencies下是否已经有
implementation fileTree(dir: 'libs', include: ['*.jar'])
第二步:配置使用SDK
1 配置HTTPDNS
//初始化配置,调用即可,不必处理返回值。
InitConfig config = InitConfig.Builder()
// 配置初始的region
.setRegion(currentRegion)
// 配置是否启用https,默认http
.setEnableHttps(enableHttps)
// 配置服务请求的超时时长,毫秒,默认2秒,最大5秒
.setTimeout(2 * 1000)
// 配置是否启用本地缓存,默认不启用
.setEnableCacheIp(true)
// 配置是否允许返回过期IP,默认允许
.setEnableExpiredIp(true)
// 配置ipv4探测域名
.setIPRankingList(ipRankingItemJson.toIPRankingList())
// 配置接口来自定义缓存的ttl时间
.configCacheTtlChanger(ttlChanger)
// 配置固定IP的域名列表,优化SDK的内部逻辑,减少解析频次
.configHostWithFixedIp(hostListWithFixedIp)
// 配置不使用HttpDns解析的域名策略
.setNotUseHttpDnsFilter(notUseHttpDnsFilter)
// 针对哪一个account配置
.build()
HttpDns.init(accountID, config);
//初始化配置,调用即可,不必处理返回值。
new InitConfig.Builder()
// 配置初始的region
.setRegion(currentRegion)
// 配置是否启用https,默认http
.setEnableHttps(enableHttps)
// 配置服务请求的超时时长,毫秒,默认2秒,最大5秒
.setTimeout(2 * 1000)
// 配置是否启用本地缓存,默认不启用
.setEnableCacheIp(true)
// 配置是否允许返回过期IP,默认允许
.setEnableExpiredIp(true)
// 配置ipv4探测域名
.setIPRankingList(list)
// 配置接口来自定义缓存的ttl时间
.configCacheTtlChanger(ttlChanger)
// 配置固定IP的域名列表,优化SDK的内部逻辑,减少解析频次
.configHostWithFixedIp(hostListWithFixedIp)
// 配置不使用HttpDns解析的域名策略
.setNotUseHttpDnsFilter(notUseHttpDnsFilter)
// 针对哪一个account配置
.buildFor(accountID);
如果初始化的时候
setEnableHttps
没有设置成true
,需要在应用级的AndroidManifest.xml
文件下的application
节点下添加配置android:usesCleartextTraffic="true"
,否则域名解析请求在高版本系统上会失败。
2 获取服务实例
HTTPDNS Android SDK以全局service实例的方式提供域名解析服务,您可以通过以下方式获取实例。
2.1 普通方式
val httpdns = HttpDns.getService(applicationContext, accountID)
HttpDnsService httpdns = HttpDns.getService(applicationContext, accountID);
2.2 鉴权方式
val httpdns = HttpDns.getService(applicationContext, accountID, secretKey)
HttpDnsService httpdns = HttpDns.getService(applicationContext, accountID, secretKey);
HTTPDNS SDK V1.1.3及以上版本支持鉴权方式,该方式需要在HTTPDNS控制台进行关闭非鉴权接口(请谨慎操作)。详情请参见鉴权解析接口。
鉴权默认过期时间为10分钟。
为避免在日志中泄漏参数accountID/secretKey或App运行过程中产生的数据,建议线上版本关闭SDK调试日志。
由于所有用户使用统一的SDK接入,在接入过程中需要在代码中设accountID/secretKey参数,而此类参数与计量计费密切相关,为防止恶意反编译获取参数造成信息泄漏,建议您开启混淆,并进行App加固后再发布上线。
3 进行域名解析
HTTPDNS提供了多种域名解析方式,包括预解析/同步解析/异步解析/同步非阻塞解析。下面以同步解析接口作为例子。
val httpDnsResult = dnsService?.getHttpDnsResultForHostSync("www.aliyun.com", RequestIpType.auto)
HTTPDNSResult httpDnsResult = httpdns.getHttpDnsResultForHostSync("www.aliyun.com", RequestIpType.auto);
请根据您的实际使用场景选择合适的域名解析接口。具体接口请查看域名解析接口和自定义解析接口。
4 使用域名解析结果
上一步解析成功后,可以拿到域名解析结果,数据结构请查看HTTPDNSResult。
此处以okhttp网络库的解析过程为例,示例代码如下:
object : Dns {
@Throws(UnknownHostException::class)
override fun lookup(host: String): List<InetAddress> {
val httpdnsResult: HTTPDNSResult = HttpDns.getService(context)
.getHttpDnsResultForHostSync(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)
}
}
new Dns() {
@Override
public List<InetAddress> lookup(String host) throws UnknownHostException {
HTTPDNSResult httpdnsResult = HttpDns.getService(context, 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 混淆配置
如果您的项目做了代码混淆,请保留以下:4 混淆配置
-keep class com.alibaba.sdk.android.**{*;}
第三步:接入验证
1 打开SDK日志
按照是否允许HTTPDNS打印Log打开HTTPDNS Sdk的日志。
2 分析日志
# 发起同步请求
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
请参考日志,分析域名解析是否成功。
注意事项
升级SDK后编译失败
如果您升级SDK版本后,出现编译失败的情况,有可能是因为SDK的API有调整,请查看Android SDK API使用替换的新接口。
务必编写降级代码
降级代码指的是当HTTPDNS无法获取期望结果时,需要降级使用Local Dns去完成域名解析。
记录从HTTPDNS获取的IP及sessionId
我们提供了用于解析问题排查的解决方案,需要您将从HTTPDNS获取的IP及sessionId记录到日志中,详情请参见如何使用“会话追踪方案”排查解析异常。
设置HTTP请求头HOST字段
标准的HTTP协议中服务端会将HTTP请求头HOST字段的值作为请求的域名信息进行解析。
使用HTTPDNS后,您可能需要将HTTP请求URL中的HOST字段替换为HTTPDNS解析获得的IP,这时标准的网络库会将您的IP赋值给HTTP请求头的HOST字段,进而导致服务端的解析异常(服务端认可的是您的域名信息,而非IP信息)。
为了解决这个问题,您可以主动设置HTTP请求HOST字段的值,如:
val originalUrl = "http://www.aliyun.com/" var url = URL(originalUrl) val originalHost = url.host // 同步非阻塞方式获取IP val httpdns = HttpDns.getService(applicationContext, accountID) val result: HTTPDNSResult = httpdns.getHttpDnsResultForHostSyncNonBlocking(originalHost, RequestIpType.auto) val conn: HttpURLConnection if (result.ips != null && result.ips.isNotEmpty()) { // 通过HTTPDNS获取IPv4成功,进行URL替换和HOST头设置 url = URL(originalUrl.replaceFirst(originalHost.toRegex(), result.ips[0])) conn = url.openConnection() as HttpURLConnection // 设置请求HOST字段 conn.setRequestProperty("Host", originalHost) } else if (result.ipv6s != null && result.ipv6s.isNotEmpty()) { // 通过HTTPDNS获取IPv4成功,进行URL替换和HOST头设置 url = URL(originalUrl.replaceFirst(originalHost.toRegex(), result.ipv6s[0])) conn = url.openConnection() as HttpURLConnection // 设置请求HOST字段 conn.setRequestProperty("Host", originalHost) } else { conn = url.openConnection() as HttpURLConnection }
String originalUrl = "http://www.aliyun.com/"; URL url = new URL(originalUrl); String originalHost = url.getHost(); // 同步非阻塞方式获取IP HttpDnsService httpdns = HttpDns.getService(applicationContext, accountID); HTTPDNSResult result = httpdns.getHttpDnsResultForHostSyncNonBlocking(originalHost, RequestIpType.auto); HttpURLConnection conn; if (result.getIps() != null && result.getIps().length > 0) { // 通过HTTPDNS获取IPv4成功,进行URL替换和HOST头设置 url = new URL(originalUrl.replaceFirst(originalHost, result.getIps()[0])); conn = (HttpURLConnection) url.openConnection(); // 设置请求HOST字段 conn.setRequestProperty("Host", originalHost); } else if (result.getIpv6s() != null && result.getIpv6s().length > 0) { // 通过HTTPDNS获取IPv4成功,进行URL替换和HOST头设置 url = new URL(originalUrl.replaceFirst(originalHost, result.getIpv6s()[0])); conn = (HttpURLConnection) url.openConnection(); // 设置请求HOST字段 conn.setRequestProperty("Host", originalHost); } else { conn = (HttpURLConnection) url.openConnection(); }
Cookie字段
部分网络库支持Cookie的自动存储管理,当您使用HTTPDNS进行IP URL请求时,部分网络库会将您URL中的IP信息作为Cookie对应的域名信息进行存储管理(而非HTTP请求头HOST字段信息),进而造成Cookie管理与使用上的困扰,因此您需要关闭Cookie的自动管理功能(默认关闭)。
HTTPS/WebView/SNI场景
HTTPS场景,参考Android端HTTPS(含SNI)业务场景:IP直连方案。
WebView场景,参考Android端HTTPDNS+Webview最佳实践。
代理情况下的使用
当存在中间HTTP代理时,客户端发起的请求中请求行会使用绝对路径的URL,在您开启HTTPDNS并采用IP URL进行访问时,中间代理将识别您的IP信息并将其作为真实访问的HOST信息传递给目标服务器,这时目标服务器将无法处理这类无真实HOST信息的HTTP请求。
移动网关提供了
X-Online-Host
的私有协议字段来解决这个问题,比如:目标URL:http://www.example.com/product/oss/ 通过HTTPDNS解析出来的www.example.com的IP:192.168.XX.XX 代理:10.0.XX.XX:XX 您的HTTP请求头: GET http://192.168.XX.XX/product/oss/ HTTP/1.1 # 通过代理发起的HTTP请求头,请求行是一个绝对路径 Host: www.example.com # 这个Header会被代理网关忽略,代理网关会使用请求行绝对路径中的host字段作为源站的host,即192.168.XX.XX X-Online-Host: www.example.com # 这个Header就是移动网关为了传递真实Host添加的私有头部,源站需要配置识别该私有头部以获取真实的Host信息
同样您可以通过
setRequestProperty
方法进行X-Online-Host
请求头域的设置,并在服务端设置对该私有头域的解析。说明在绝大多数场景下,我们建议您在代理模式下关闭HTTPDNS功能。
集成常见问题
UTDID冲突,可参考:阿里云-云产品SDK UTDID冲突解决方案