如果您使用<code>httpclient</code>或<code>httpurlconnection</code>发起网络请求,尽管无法直接自定义dns服务,但是由于<code>httpclient</code>和<code>httpurlconnection</code>也通过<code>inetaddress</code>进行域名解析,通过修改<code>inetaddress</code>的dns缓存,同样可以比通用方案更为优雅地使用httpdns。
核心代码位于<code>java.net.inetaddress.lookuphostbyname(string host, int netid)</code>
其中<code>addresscache</code>为<code>inetaddress</code>的本地缓存:
结合<code>inetaddress</code>的解析策略,我们可以通过如下方法实现自定义dns服务:
通过httpdns sdk获取目标域名的ip
利用反射的方式获取到<code>inetaddress.addresscache</code>对象
利用反射方式调用<code>addresscache.put()</code>方法,域名和ip的对应关系写入<code>inetaddress</code>缓存
具体实现可参考以下代码:
和通用方案相比,使用该方法具有下列优势:
实现简单
通用性强,该方案在https,sni以及设置cookie等场景均适用。规避了证书校验,域名检查等环节
全局生效,<code>inetaddress.addresscache</code>为全局单例,该方案对所有使用<code>inetaddress</code>作为域名解析服务的请求全部生效
另外使用该方案请务必注意以下几点:
<code>addresscache</code>的默认ttl为2s,且默认最多可以保存16条缓存记录:
android虚拟机下反射规则与jvm存在差异,无法直接修改final变量的值。所以使用该方法请务必注意ip过期时间及缓存数量。另外针对该问题可尝试另一种解决方案:重写addresscache类,并通过classloader优先加载,覆盖系统类。
<code>addresscache.put</code>方法在 api 21进行了改动,增加了<code>netid</code>参数,为保证兼容性需要针对不同版本区别处理。具体方案参考上文代码
该方式可以解决https,sni以及设置cookie等场景,但不适用于webview场景。android webview使用<code>chromium</code>或<code>webkit</code>作为内核(android 4.4开始,webview内核由chromium替代webkit)。上述两者均绕开inetaddress而直接使用系统dns服务,所以该方案对此场景无效。