HTTPDNS Android SDK

Updated at:
Copy as MD

Integrate the HTTPDNS Android SDK into your app for reliable domain resolution with built-in caching.

Overview

The Android SDK is a Java wrapper for the HTTPDNS DoH JSON API. It provides Java interfaces for Android apps to resolve domain names and includes an efficient local cache that uses Time-to-Live (TTL) and Least Recently Used (LRU) policies. You can easily integrate HTTPDNS into your Android app to fix domain resolution errors and enable precise, low-cost scheduling.

Key advantages:

  • Simple to use

    Minimal code required to access the HTTPDNS service.

  • Zero latency

    The LRU cache stores resolved IPs locally and proactively refreshes entries before TTL expiry, enabling zero-latency resolution.

Download the alidns_android_demo sample project for a working implementation reference.

SDK integration

Add the SDK

Gradle and Maven

Add the following code to your build.gradle file:

allprojects {
  repositories {
    maven {
      url 'https://maven.aliyun.com/repository/public/'
    }
    mavenLocal()
    mavenCentral()
  }
}

Add the dependency information:

dependencies {
     implementation 'com.alibaba.pdns:alidns-android-sdk:2.3.2'
     implementation 'com.google.code.gson:gson:2.8.5'
}

AAR file

Download the SDK from the Download SDK page, then add the alidns_android_sdk.aar package to your project's libs directory.

SDK initialization

Important

Initialize the SDK as early as possible in your application lifecycle to prevent resolution failures.

Get your Account ID from the console and create a key to obtain your AccessKey ID and AccessKey Secret. Then initialize the SDK as shown in this Application class example:

public class DnsCacheApplication extends Application{

    private String accountId = "Your Account ID"; // Set your Account ID from the console.
    private String accessKeyId = "Your AccessKey ID"; // Set your AccessKey ID from the console.
    private String accessKeySecret = "Your AccessKey Secret"; // Set your AccessKey Secret from the console.

    @Override
    public void onCreate() {
       super.onCreate();
       // Set the Account ID, AccessKey ID, and AccessKey Secret for SDK access.
       DNSResolver.Init(this, accountId, accessKeyId, accessKeySecret); 
       // Note: If you configure domains for keep-alive, resolution is automatically triggered when 75% of the TTL elapses. 
       // This ensures that resolution for these domains always hits the cache. However, if you use a CDN, 
       // the TTL can be short, leading to frequent requests and increased costs. Use this method with caution.
       DNSResolver.setKeepAliveDomains(new String[]{"your-domain-to-keep-alive-1","your-domain-to-keep-alive-2",...});       
       // Perform pre-resolution for specified domains to get IPv4 addresses. Replace the placeholder domains with the ones you want to resolve.
       DNSResolver.getInstance().preLoadDomains(DNSResolver.QTYPE_IPV4,new String[]{"your-domain-to-preload-1","your-domain-to-preload-2",...}); 
    }
}
Note

DNSResolver is the core class of the HTTPDNS SDK. It wraps the HTTPDNS DoH JSON API to resolve domain names to IP addresses. Initialize the HTTPDNS SDK in your Application subclass.

Declare the following permissions in AndroidManifest.xml:

<!--Required permissions-->
   <uses-permission android:name="android.permission.INTERNET"/>
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
   <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

SDK authentication

Starting from version 2.0, the SDK requires authentication to prevent unauthorized use. To enable HTTPDNS, create a key in the console to get an AccessKey ID and an AccessKey Secret. You must set authentication parameters during SDK initialization. HTTPDNS rejects unauthenticated requests, which causes resolution to fail and impacts your services.HTTPDNS

You can set the authentication parameters as follows:

DNSResolver.Init(this, accountId, accessKeyId, accessKeySecret);
Warning
  • To prevent your Account ID, AccessKey ID, AccessKey Secret, or other runtime data from being exposed in logs, disable SDK debug logging in your production release.

  • The SDK integration requires setting the Account ID, AccessKey ID, and AccessKey Secret in your code. These parameters are linked to metering and billing. To prevent malicious decompilation from exposing your credentials, enable code obfuscation and app reinforcement before releasing your application.

SDK integration issues

"Cleartext HTTP traffic not permitted" error

Cause: Starting from Android 9.0 (API level 28), Android blocks cleartext network traffic by default and allows only HTTPS URLs.

Solution

In your AndroidManifest.xml file, add the following code to the element:

android:usesCleartextTraffic="true"

<application
        android:name=".DnsCacheApplication"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:usesCleartextTraffic="true">

"Didn't find class BasicHttpParams" error

Cause: The Apache HTTP client is deprecated.

In Android 6.0, Google removed support for the Apache HTTP client. Starting from Android 9.0, org.apache.http.legacy is removed from the bootclasspath.

This change does not affect most applications that target an API level lower than 28. For applications that target API level 28 or higher, if you continue to use the Apache HTTP interface or a library that uses it, an exception occurs because the Apache HTTP interface cannot be found.

Solution

In your AndroidManifest.xml file, add the following code to the element:

<uses-library android:name="org.apache.http.legacy" android:required="false"/>

NDK configuration

  • In the root directory of your app project, add the NDK installation path to the local.properties file.

ndk.dir=...\\ndk\\21.4.7075529; // Replace ... with the local NDK installation path.
  • In the root directory of your app project, add the following configuration to the gradle.properties file.

android.useDeprecatedNdk = true;

For optimal performance, we recommend JDK 1.8 and NDK 21.4.7075529 to compile your app.

API reference

Common settings

1. Initialize by using the Init method

Call the Init method when you initialize the SDK in your Application class.

DNSResolver.Init(this, accountId, accessKeyId, accessKeySecret);

2. Set domains for pre-resolution

Register domains with the HTTPDNS SDK during initialization to enable pre-resolution and reduce subsequent latency:

  • Specify pre-resolution for IPv4 or IPv6 domains

// Specify the record type for pre-resolution. Replace the placeholder domains with the ones you want to resolve.
DNSResolver.getInstance().preLoadDomains(DNSResolver.QTYPE_IPV4,new String[]{...});

// DNSResolver.QTYPE_IPV4: Pre-fetches the IPv4 record type for the domain.
// DNSResolver.QTYPE_IPV6: Pre-fetches the IPv6 record type for the domain.
// DNSResolver.QTYPE_IPV4_IPV6: Pre-fetches both IPv4 and IPv6 record types for the domain.
  • Automatically select IPv4 or IPv6 for pre-resolution based on the current network. In a dual-stack environment, both IPv4 and IPv6 addresses are pre-loaded.

DNSResolver.getInstance().preLoadDomains(domains);
Important

The pre-resolution API triggers asynchronous network requests in real time. Make sure that all necessary initialization is complete before you call this API.

3. Set domains for cache keep-alive

The SDK re-resolves configured domains at 75% TTL expiry to keep the cache current. We recommend limiting this to 10 domains. Independent of pre-resolution.

DNSResolver.setKeepAliveDomains(new String[]{"your-domain-1", "your-domain-2"});
Note

Pros

1. Records are updated in a timely manner (before the TTL expires).

2. When used with pre-resolution, initial resolution latency can be reduced to 0 ms.

Cons

1. Re-requesting at 75% of the TTL incurs additional costs.

4. Specify whether to use an IPv6 server address

The HTTPDNS service supports both IPv4 and IPv6 access. Use DNSResolver.setEnableIPv6(boolean enable) to toggle IPv6 server access. Default: IPv4. If IPv6 is enabled and the connection to the HTTPDNS service fails, the SDK automatically retries over IPv4 once.

5. Set the maximum number of cache entries

DNSResolver.getInstance().setMaxCacheSize(CACHE_MAX_NUMBER); // Sets the maximum number of cache entries. The default is 100.

You can customize the maximum count value.

6. Set the protocol for server access

The SDK supports HTTP and HTTPS for resolution requests. Default: HTTPS (recommended for security). Note that HTTPS requests are billed at five times the HTTP rate. Choose based on your business requirements.HTTPDNS

DNSResolver.setSchemaType(DNSResolver.HTTPS); // The default access mode is HTTPS.

DNSResolver.HTTP: Accesses server-side interfaces over HTTP.

DNSResolver.HTTPS: Accesses server-side interfaces over HTTPS.

Advanced settings

1. Enable or disable SDK debug logging

DNSResolver.setEnableLogger(true); // SDK debug logs are disabled by default.

Enable or disable SDK debug logging. Default: disabled.

2. Specify whether to enable automatic HTTPDNS to local DNS when HTTPDNS resolution fails

DNSResolver.setEnableLocalDns(true); // By default, automatic fallback to local DNS is enabled when HTTPDNS resolution fails.

3. Enable or disable short mode

The HTTPDNS DoH JSON API returns data in two formats: full JSON and a compact IP array. You can call DNSResolver.setEnableShort(boolean enable) to enable or disable short mode. By default, short mode is disabled.

DNSResolver.setEnableShort(true); // The default value is false. You do not need to set this parameter.
Important

In short mode, the HTTPDNS SDK returns a compact IP array instead of full JSON, reducing response size. Suitable for traffic-sensitive scenarios.

4. Specify whether to enable a non-expiring cache

DNSResolver.setImmutableCacheEnable(false); // By default, the non-expiring cache is disabled.
Important

The SDK provides three cache update mechanisms:

  • Cache never expires: When enabled, this feature treats the cache as always valid during app runtime by skipping expiration checks and update operations. You do not need to set setKeepAliveDomains to actively update the cache, which minimizes the number of user resolutions.

    Method: DNSResolver.setImmutableCacheEnable(boolean var0)

    When the parameter var0 is true, the cache never expires feature is enabled. When var0 is false, this feature is disabled.

  • Active cache update: This feature ensures that resolutions hit the most up-to-date cached records. When the authoritative resolution for a domain changes, this mechanism ensures that resolution requests not only hit the cache to reduce DNS latency but also retrieve the latest records. When 75% of a domain's TTL elapses, the SDK automatically triggers a resolution query to update the cache. We recommend that you limit the number of domains for which active updates are enabled to 10.

    Method: DNSResolver.setKeepAliveDomains(String[] var1)

    Description: var1 is a string array of domain names that need to be proactively updated.

  • Passive cache update:

    When you call the following two methods to get resolution results, the cache is passively updated:

    • The getIPsV4ByHost(String hostName) method retrieves an array of IPv4 records for the specified hostName. If the cache is not empty and is within its TTL, the method returns the cached result directly. Otherwise, it first retrieves the latest resolution result through a network request, and then returns that result and updates the cache. This method is often used in scenarios that require highly accurate resolution results.

    • The getIpv4ByHostFromCache(String hostName, boolean isAllowExp) method retrieves an array of IPv4 records for a hostName from the cache. Based on the value of the isAllowExp parameter, this method determines whether to return expired resolution results from the cache. We recommend that you use this method with a preload method at app startup to ensure that the latest resolution results are cached.

      If isAllowExp is set to true, stale data is returned even if the cache has expired (null is returned if the cache is empty), and the cache is updated by using an asynchronous request. If this parameter is set to false, null is returned when the cache is expired or empty, and the cache is updated by using an asynchronous request.

    Recommended pattern:

    String[] IPArray = mDNSResolver.getIpv4ByHostFromCache(hostname,true);
            if (IPArray == null || IPArray.length == 0){
                IPArray = mDNSResolver.getIPsV4ByHost(hostname);
            }

5. Enable or disable the cache

DNSResolver.setEnableCache(true); // The cache is enabled by default.

Enable or disable the resolution cache. Default: enabled.

6. Enable or disable IP speed testing. This feature is disabled by default in versions 2.3.0 and earlier, and enabled by default in versions 2.3.1 and later.

DNSResolver.setEnableSpeedTest(false); // Disabled by default in v2.3.0 and earlier; enabled by default in v2.3.1 and later.

Enable or disable IP speed testing. Default: disabled in v2.3.0 and earlier, enabled in v2.3.1+.

7. Set the port number for IP speed testing via socket monitoring

DNSResolver.setSpeedPort(DNSResolver.PORT_80);

Set the port for socket-based IP speed testing. Default: 80.

8. Specify whether to partition the domain cache by ISP network

DNSResolver.setIspEnable(true); // By default, the domain cache is partitioned by ISP network.

When enabled, domain cache data is stored separately per network environment. When disabled, a single cache is shared across all networks.

9. Set the maximum TTL for the negative cache

DNSResolver.setMaxNegativeCache(MAX_NEGATIVE_CACHE); // Sets the maximum TTL for the negative cache. The default is 30 seconds.

Set the maximum TTL for the negative cache. A negative cache entry is created when no IP is returned for a domain. Default: 30 seconds.

10. Set the maximum TTL for the cache

DNSResolver.setMaxTtlCache(MAX_TTL_CACHE); // Sets the maximum TTL for the cache. The default value is 3,600 seconds.

Set a maximum TTL cap for cache entries. Default: 3,600 seconds.

11. Set client subnet information

DNSResolver.setEdnsSubnet("1.2.XX.XX/24");

setEdnsSubnet supports EDNS Client Subnet (ECS, RFC 7871), passing user subnet information to authoritative DNS for precise traffic scheduling. A longer mask provides more precise results; a shorter mask improves privacy. A /24 mask is recommended.

Note

This parameter is designed for scenarios where a DNS proxy uses the DoH JSON API. In these scenarios, a user sends a DNS query to the DNS proxy, and the proxy uses this parameter to pass the user's subnet information to HTTPDNS and then to the authoritative DNS server.

For example, if you call DNSResolver.setEdnsSubnet("1.2.XX.XX/24"), the authoritative server receives prefix information based on the 1.2.XX.XX/24 address to help you select a DNS link.

12. Set the timeout period for domain name resolution

Set the timeout for domain resolution. Default: 3 seconds. Recommended range: 2–5 seconds.

DNSResolver.setTimeout(3);

13. Set the maximum number of concurrent resolutions (supported in v2.3.2 and later)

Set the maximum number of concurrent async DNS resolutions (pre-resolution, cache refresh). Valid range: [1, 50]. Default: 10.

DNSResolver.setMaxConcurrentResolveCount(10);

14. Get SessionId for troubleshooting

A sessionId is generated at app startup and remains constant throughout the lifecycle. All HTTPDNS resolution requests carry this ID. Use it to trace requests on the server for troubleshooting.

public static String getSessionId()

15. Log output callback

This callback receives the logs that are output by the SDK.

HttpDnsLog.setLogger(new ILogger() {
  @Override
  public void log(String msg) {
      Log.d("HttpDnsLogger:", msg);
  }
});

ProGuard configuration

   -keep class com.alibaba.pdns.** {*;}

Service API

  /**
   * Pre-loads domain name resolution based on the automatically detected network environment (IPv4-only, IPv6-only, or dual-stack).
   * In a dual-stack network environment, both IPv4 and IPv6 resolution results are pre-loaded. You can call this method during SDK initialization at app startup.
   * This method pre-stores the resolution results in the cache to reduce the latency of subsequent domain name resolutions.
   *
   * @param domains The domain names to be pre-loaded.
   */
   public void preLoadDomains(final String[] domains)

    /**
     * Pre-loads domain resolution for a specified record type (IPv4 or IPv6). 
     * You can call this method during SDK initialization at app startup. This method pre-stores the resolution results in the cache to reduce latency for later resolutions.
     *
     * @param qType The record type to pre-load, such as IPv4 or IPv6.
     * @param domains The domain names to be pre-loaded.
     */
    public void preLoadDomains(String qType, final String[] domains)
   /**
   * Obtains the resolution data for the domain name based on the automatically detected network environment (IPv4-only, IPv6-only, or dual-stack).
   * If a non-expired resolution result exists in the cache, the result from the cache is returned.
   * If the cache is empty or the cached result has expired, a synchronous network request is sent to the server to obtain the recursive resolution result. The result is then returned and stored in the cache.
   *
   * @param host The domain name that you want to resolve.
   * @return The optimal IP address array based on the current network environment.
   */
    public String[] getIpsByHost(String host)

/**
    * Obtains the array of IPv4 records that correspond to the hostname.
    * If a non-expired resolution result exists in the cache, the result from the cache is returned.
    * If the cache is empty or the cached result has expired, a synchronous network request is sent to the server to obtain the recursive resolution result. The result is then returned and stored in the cache.
    *
    * @param hostName The hostname, such as www.example.com.
    * @return The array of IPv4 addresses that correspond to the hostname.
    */
  public  String[] getIPsV4ByHost(String hostName) 

   /**
    * Obtains the array of IPv6 records that correspond to the hostname.
    * @param hostName The hostname, such as www.example.com.
    * @return The array of IPv6 addresses that correspond to the hostname.
    */
  public String[] getIPsV6ByHost(String hostName) 

  

  /**
   * Obtains the IP address array for the resolved domain name from the cache based on the automatically detected network environment (IPv4-only, IPv6-only, or dual-stack).
   * If the cache is empty, this method returns null and initiates an asynchronous query. The query result is then stored in the cache.
   * If a resolution result exists in the cache and you allow returning expired results, the expired IP addresses are returned, and the cache is updated asynchronously.
   * If you do not allow returning expired results and the cached result has expired, this method returns null and then asynchronously updates the cache.
   *
   * @param host The host to query, such as www.example.com.
   * @param isAllowExp Specifies whether to return the resolution data of an expired domain.
   * @return The cached IP address array for the resolved host.
   */
   public String[] getIpsByHostFromCache(String host, boolean isAllowExp)
   
    /**
     * Obtains the IP address array of the IPv4 record type for the resolved domain name from the cache.
     * If the cache is empty, this method returns null and initiates an asynchronous query. The query result is then stored in the cache.
     * If a resolution result exists in the cache and you allow returning expired results, the expired IP addresses are returned, and the cache is updated asynchronously.
     * If you do not allow returning expired results and the cached result has expired, this method returns null and then asynchronously updates the cache.
     *
     * @param host The host to query, such as www.example.com.
     * @param isAllowExp Specifies whether to return the resolution data of an expired domain.
     * @return The IP address array of the IPv4 record type from the cache after the host is resolved.
     */
    public String[] getIpv4ByHostFromCache(String host , boolean isAllowExp)

    /**
     * Obtains the IP address array of the IPv6 record type for the resolved domain name from the cache.
     * If the cache is empty, this method returns null and initiates an asynchronous query. The query result is then stored in the cache.
     * If a resolution result exists in the cache and you allow returning expired results, the expired IP addresses are returned, and the cache is updated asynchronously.
     * If you do not allow returning expired results and the cached result has expired, this method returns null and then asynchronously updates the cache.
     *
     * @param host The host to query, such as www.example.com.
     * @param isAllowExp Specifies whether to return the resolution data of an expired domain.
     * @return The IP address array of the IPv6 record type from the cache after the host is resolved.
     */
    public String[] getIpv6ByHostFromCache(String host , boolean isAllowExp)

  /**
  * Obtains the array of DomainInfo objects for IPv4 records that correspond to the URL.
  * If a non-expired resolution result exists in the cache, the result from the cache is returned.
  * If the cache is empty or the cached result has expired, a synchronous network request is sent to the server to obtain the recursive resolution result. The result is then returned and stored in the cache.
  *
  * @param url The URL, such as http://www.example.com.
  * @return The array of DomainInfo objects of the IPv4 type that correspond to the URL.
  */
  public DomainInfo[] getIPsV4DInfoByUrl(String url) 

  Note: The URL in the DomainInfo object is the URL in which the host is automatically replaced with an IP address. You do not need to manually replace the host in the URL.

  /**
   * Obtains the array of DomainInfo objects for IPv6 records that correspond to the URL.
   * If a non-expired resolution result exists in the cache, the result from the cache is returned.
   * If the cache is empty or the cached result has expired, a synchronous network request is sent to the server to obtain the recursive resolution result. The result is then returned and stored in the cache.
   * 
   * @param url The URL, such as http://m.example.com.
   * @return The array of DomainInfo objects of the IPv6 type that correspond to the URL.
   */
   public DomainInfo[] getIPsV6DInfoByUrl(String url) 

  /**
    * Obtains a DomainInfo object for the IPv4 records that correspond to a specific URL.
    * If a non-expired resolution result exists in the cache, the result from the cache is returned.
    * If the cache is empty or the cached result has expired, a synchronous network request is sent to the server to obtain the recursive resolution result. The result is then returned and stored in the cache.
    *
    * @param url The URL, such as http://m.example.com.
    * @return A random DomainInfo object from the collection of IPv4-type DomainInfo objects that correspond to the URL.
    */
    public DomainInfo getIPV4DInfoByUrl(String url) 


  /**
    * Obtains a DomainInfo object for the IPv6 records that correspond to a specific URL.
    * If a non-expired resolution result exists in the cache, the result from the cache is returned.
    * If the cache is empty or the cached result has expired, a synchronous network request is sent to the server to obtain the recursive resolution result. The result is then returned and stored in the cache.
    *
    * @param url The URL, such as http://www.example.com.
    * @return A random DomainInfo object from the collection of IPv6-type DomainInfo objects that correspond to the URL.
    */
   public DomainInfo getIPV6DInfoByUrl(String url) 

   Note: The returned DomainInfo object encapsulates the following properties.

  /**
   * The auto-incrementing ID for the access domain.
    */
    public String id = null;

   /**
    * The URL that can be directly used. The host in the URL is replaced with an IP address.
    */
     public String url = null;

    /**
    * The name of the destination service to be set in the HTTP header.
    */
    public String host = "";

   /**
    * The returned content body.
    */
   public String data = null;

   /**
    * The time when the request starts.
    */
   public String startTime = null;

   /**
    * The time when the request ends. If the request times out, this value is null.
    */
   public String stopTime = null; 

   /**
   * The status code returned by the server, such as 200, 404, or 500. 
   */
   public String code = null;

  /**
    * Obtains the IPv4 record that corresponds to the hostname.
    * If a non-expired resolution result exists in the cache, the result from the cache is returned.
     * If the cache is empty or the cached result has expired, a synchronous network request is sent to the server to obtain the recursive resolution result. The result is then returned and stored in the cache.
  
    * @param hostName The hostname, such as www.example.com.
    * @return A random IPv4 address from the set of IPv4 addresses that correspond to the hostname. If IP speed testing is enabled, the optimal IPv4 address is returned.
    */
  public String getIPV4ByHost(String hostName) 

   /**
    * Obtains the IPv6 record that corresponds to the hostname.
    * If a non-expired resolution result exists in the cache, the result from the cache is returned.
    * If the cache is empty or the cached result has expired, a synchronous network request is sent to the server to obtain the recursive resolution result. The result is then returned and stored in the cache.
  
    * @param hostName The hostname, such as www.example.com.
    * @return A random IPv6 address from the set of IPv6 addresses that correspond to the hostname. If IP speed testing is enabled, the optimal IPv6 address is returned.
    */
   public String getIPV6ByHost(String hostName) 
      

    /**
     * Obtains the statistics about successful and failed HTTPDNS requests.
     *
     * @return A JSON array string of the resolution statistics for all domain names.
     */
    public String getRequestReportInfo()
    
     /**
     * Sets domain names for cache keep-alive. The resolution of a configured domain name is automatically initiated when 75% of the TTL elapses. This ensures that resolution requests for the configured domain name always hit the cache and improves the resolution efficiency of the SDK.
     * We recommend that you do not configure an excessive number of domain names for this feature. The current limit is 10 domain names. This feature is configured independently of the pre-resolution feature.
     *
     * @param persistentCacheDomains
     */
    public synchronized static void setKeepAliveDomains(String[] persistentCacheDomains) {
    
     /**
     * Clears the cache for specified domain names. If the hostname is null, the cache for all domain names is cleared.
     *
     * @param domains The array of domain names whose cache you want to clear.
     */
    public void clearHostCache(String[] domains){

API examples

URL: The access address that is passed in, for example, http://www.example.com.

 String hostname = "www.taobao.com";
 String url = "http://www.taobao.com";

1. Get the optimal IP data for the current network environment

String[] ip = DNSResolver.getInstance().getIpsByHost(hostname); // Gets the optimal domain resolution IP for the current network.

2. Pre-load domain resolution based on the current network environment

DNSResolver.getInstance().preLoadDomains(domains); // Sets domain names for pre-resolution. Replace the placeholder domains with the ones you want HTTPDNS to resolve.

3. Read domain resolution data from the cache based on the current network environment

String[] ip = DNSResolver.getInstance().getIpsByHostFromCache(hostname,true); // Gets domain resolution data from the cache for the current network environment.

4. Get an IPv4 address

String IPV4 = DNSResolver.getInstance().getIPV4ByHost(hostname); // Gets the resolved IPv4 address.

5. Get an IPv6 address

String IPV6 =  DNSResolver.getInstance().getIPV6ByHost(hostname); // Gets the resolved IPv6 address.

6. Get a resolved IPv4 address from the cache

String[] IPV4 =  DNSResolver.getInstance().getIpv4ByHostFromCache(hostname , true); // Gets the resolved IPv4 address from the cache.

7. Get a resolved IPv6 address from the cache

String[] IPV6 =  DNSResolver.getInstance().getIpv6ByHostFromCache(hostname , true); // Gets the resolved IPv6 address from the cache.

8. Get the DomainInfo object that corresponds to a URL

DomainInfo dinfo = DNSResolver.getInstance().getIPV4DInfoByUrl(url); // Gets the replaced URL.

9. Clear the resolution result of a specified domain from the cache

DNSResolver.getInstance().clearHostCache(hostName); // Clears the cache for a specified domain. If hostName is set to null, the cache for all domains is cleared.

10. Get statistics about successful and failed HTTPDNS requests

String reportInfo = DNSResolver.getInstance().getRequestReportInfo(); // Gets statistics about successful and failed requests.

The following table describes the fields in the JSON string array of domain resolution statistics.

 [
      {
         "avgRtt":"1",                         // Average domain resolution time, in milliseconds (ms).
         "degradeLocalDnsCount": 0,            // Number of fallbacks to local DNS.                       
         "domainName":"www.example.com",       // The resolved domain name.
         "hitDnsCacheCount": 1,                // Number of cache hits.
         "httpabnormalCount": 0,               // Number of failed recursive requests.
         "isp": "China Mobile",                // ISP name.
         "localDnsResolveErrCount": 0,         // Number of local DNS resolution failures.
         "maxRtt": 8.0,                        // Maximum domain resolution time, in milliseconds (ms).          
         "nonetworkCount": 0,                  // Number of times the network is unavailable.
         "permissionErrCount": 0,              // Number of user authentication failures.
         "queryType": 1,                       // IP type. 1 indicates IPv4, and 28 indicates IPv6.
         "recursiveReqCount": 1,               // Number of recursive queries.
         "reqParameterErrCount": 0,            // Number of request parameter format errors.
         "reqPathErrCount": 0,                 // Number of URL errors.
         "reqServerErrCount": 0,               // Number of DNS server-side errors.
         "reqTimeoutCount": 0,                 // Number of DNS service timeout errors.
         "resolveSuccessCount": 1,             // Number of successful resolutions.
         "timeoutCount": 0,                    // Number of network timeout errors.
         "utfNetWorkErroNum": 0                // Number of data reporting timeout errors.
      }
         ......
 ]
Important

Statistics for HTTPDNS domain resolution requests are aggregated by network environment, domain name, and request type.

Example

public class MainActivity extends AppCompatActivity {
   private Button button;
   private TextView tvInfo;
   private TextView tvResult;
   private String hostUrl = "http://www.taobao.com"; // Replace this with the hostUrl that you want to resolve.
   private String hostName = "www.taobao.com"; // Replace this with the hostName that you want to resolve.
   private static final String TAG = "PDnsDemo";
   private static ExecutorService pool = Executors.newSingleThreadExecutor();
   private static final String PDNS_RESULT = "pdns_result";
   private static final int SHOW_CONSOLE_TEXT = 10000;
   private Handler mHandler;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.demo_activity_main);
       init();
       initHandler();
   }
 private void init() {
       tvInfo = findViewById(R.id.tv_respons_info);
       tvResult = findViewById(R.id.tv_respons);
       button = findViewById(R.id.btn_onclik);
       button.setOnClickListener(new View.OnClickListener() {
           public void onClick(View view) {
               new Thread(new Runnable() {
                      @Override
                      public void  run() {
                      // Call the getIPV4ByHost method in the HTTPDNS SDK to obtain the resolved IP address of the target domain name.
                      String ip = DNSResolver.getInstance().getIPV4ByHost(hostName);
                      if(ip != null){
                         tvInfo.setText("The resolved IP for the domain is: "+ ip);
                      }
                      // Call the getIPV4DInfoByUrl method in the HTTPDNS SDK to get the URL from the resolved DomainInfo object. 
                      // The host in this URL is replaced with the IP address.
                      DomainInfo dinfo = DNSResolver.getInstance().getIPV4DInfoByUrl(hostUrl);
                      if (dinfo != null) {
                           showResponse(dinfo);
                      }
                   }
               }).start();
           }
       });
   }
   private void initHandler() {
       mHandler = new Handler() {
           @Override
           public void handleMessage(Message msg) {
               switch (msg.what)  {
                   case SHOW_CONSOLE_TEXT:
                       tvResult.setText(msg.getData().getString(PDNS_RESULT) + "\n");
                       break;
               }
           }
       };
   }
   private void showResponse(final DomainInfo dinfo) {
                // Sends a network request.
               String requestUrl = dinfo.url;
               HttpURLConnection conn = null;
               try {
                   URL url = new URL(requestUrl);
                   conn = (HttpURLConnection) url.openConnection();
                   // When you use an IP address for access, you must set the Host field of the HTTP request header to the original domain name.
                   conn.setRequestProperty("Host", url.getHost()); // Sets the Host field of the HTTP request header.
                   DataInputStream dis = new DataInputStream(conn.getInputStream());
                   int len;
                   byte[] buff = new byte[4096];
                   StringBuilder response = new StringBuilder();
                   while ((len = dis.read(buff)) != -1) {
                       response.append(new String(buff, 0, len));
                   }
                   Log.d(TAG, "Response: " + response.toString());
                   dis.close();
                   sendMessage(response.toString());
               } catch (IOException e) {
                   e.printStackTrace();
               }finally {
                   if (conn != null) {
                       conn.disconnect();
                   }
               }
           }
   private void sendMessage(String message) {
       if (mHandler != null) {
               Message msg = mHandler.obtainMessage();
               Bundle bundle = new Bundle();
               bundle.putString(PDNS_RESULT, message);
               msg.setData(bundle);
               msg.what = SHOW_CONSOLE_TEXT;
               mHandler.sendMessage(msg);
       }
   }
}

public class DnsCacheApplication extends Application {
    
    private String accountId = "Your Account ID"; // Set your Account ID from the console.
    private String accessKeyId = "Your AccessKey ID"; // Set your AccessKey ID from the console.
    private String accessKeySecret = "Your AccessKey Secret"; // Set your AccessKey Secret from the console.

    @Override
    public void onCreate() {
       super.onCreate();
       // Set the Account ID, AccessKey ID, and AccessKey Secret for SDK access.
       DNSResolver.Init(this, accountId, accessKeyId, accessKeySecret); 
       // Sets domain names for cache keep-alive. The resolution of a configured domain name is automatically initiated at 75% of its TTL to ensure that resolution requests for the domain name always hit the cache.
       DNSResolver.setKeepAliveDomains(new String[]{"your-domain-1","your-domain-2",...}); 
       // Pre-loads specified domains to IPv4 addresses. Replace the placeholder domains with the ones that you want to resolve.
       DNSResolver.getInstance().preLoadDomains(DNSResolver.QTYPE_IPV4,new String[]{"your-domain-to-preload-1","your-domain-to-preload-2",...}); 
    }
}

Best practices

Combine pre-resolution with expired-response caching for optimal performance.

Pre-resolution caches results at startup. Allowing expired responses returns cached IPs immediately even after TTL expiry, achieving near-zero-latency resolution.

Pre-loaded domains hit the local cache on subsequent requests, eliminating network round-trips.

1. Pre-resolution

Enable caching and pre-resolve key domains at application startup.

In Application's onCreate() method, pre-resolve your domains and cache the results locally.

1. IPv4-only scenarios

//********For IPv4-only scenarios*******
public class DnsCacheApplication extends Application{
    private String accountId = "Your Account ID"; // Set your Account ID from the console.
    private String accessKeyId = "Your AccessKey ID"; // Set your AccessKey ID from the console.
    private String accessKeySecret = "Your AccessKey Secret"; // Set your AccessKey Secret from the console.

    @Override
    public void onCreate() {
       super.onCreate();
       // Set the Account ID, AccessKey ID, and AccessKey Secret for SDK access.
       DNSResolver.Init(this, accountId, accessKeyId, accessKeySecret); 
       DNSResolver.setEnableCache(true); // Enable caching. Default value: true. 
       // The IPv4 record type for pre-resolution.    
       // Pre-loads specified domains to IPv4 addresses. Replace the placeholder domains with the ones that you want HTTPDNS to resolve.
       DNSResolver.getInstance().preLoadDomains(DNSResolver.QTYPE_IPV4,new String[]{"your-domain-to-preload-1","your-domain-to-preload-2",...}); 
    }
}

2. IPv6 support

//********For scenarios that require IPv6 support*******
public class DnsCacheApplication extends Application{
    private String accountId = "Your Account ID"; // Set your Account ID from the console.
    private String accessKeyId = "Your AccessKey ID"; // Set your AccessKey ID from the console.
    private String accessKeySecret = "Your AccessKey Secret"; // Set your AccessKey Secret from the console.

    @Override
    public void onCreate() {
       super.onCreate();
       // Set the Account ID, AccessKey ID, and AccessKey Secret for SDK access.
       DNSResolver.Init(this, accountId, accessKeyId, accessKeySecret); 
       DNSResolver.setEnableCache(true); // Enable caching. Default value: true.
       DNSResolver.setEnableIPv6(true); // Specifies whether to resolve domain names over an IPv6 network. Default value: false.  
       DNSResolver.setEnableSpeedTest(true); // Specifies whether to enable IP speed testing. Default value: false.
       // The IPv4 and IPv6 record types for pre-resolution.  
       // Pre-loads specified domains to IPv4 and IPv6 addresses. Replace the placeholder domains with the ones that you want HTTPDNS to resolve.
       DNSResolver.getInstance().preLoadDomains(DNSResolver.QTYPE_IPV4_IPV6,new String[]{"your-domain-to-preload-1","your-domain-to-preload-2",...}); 
    }
}

2. Allow expired responses

Read IPs from cache before network requests, allowing expired entries to be returned immediately. The cache is updated asynchronously in the background.

1. IPv4-only scenarios

    //********For IPv4-only scenarios*******
    @Override
    public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
        // Prioritize getting the IP address from the cache. The second parameter, if true, allows returning expired but usable records.
        String[] IPArray = mDNSResolver.getIpv4ByHostFromCache(hostname,true);
        if (IPArray == null || IPArray.length == 0){
            // If the cache is not hit, initiate an asynchronous resolution.
            IPArray = mDNSResolver.getIPsV4ByHost(hostname);
        }
        if (IPArray != null && IPArray.length > 0) {
            List<InetAddress> inetAddresses = new ArrayList<>();
            InetAddress address;
            for (String ip : IPArray) {
                address = InetAddress.getByName(ip);
                inetAddresses.add(address);
            }
            if (!inetAddresses.isEmpty()) {
                return inetAddresses;
            }
        }
        return okhttp3.Dns.SYSTEM.lookup(hostname);
    }

2. IPv6 support

    //********For scenarios that require IPv6 support*******
    @Override
    public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
        // Prioritize getting the IP address from the cache. The second parameter, if true, allows returning expired but usable records.
        String[] IPArray = mDNSResolver.getIpsByHostFromCache(hostname,true);
        if (IPArray == null || IPArray.length == 0){
            // If the cache is not hit, initiate an asynchronous resolution.
            IPArray = mDNSResolver.getIpsByHost(hostname);
        }
        if (IPArray != null && IPArray.length > 0) {
            List<InetAddress> inetAddresses = new ArrayList<>();
            InetAddress address;
            for (String ip : IPArray) {
                address = InetAddress.getByName(ip);
                inetAddresses.add(address);
            }
            if (!inetAddresses.isEmpty()) {
                return inetAddresses;
            }
        }
        return okhttp3.Dns.SYSTEM.lookup(hostname);
    }

Usage notes

  1. When using an IP address obtained from HTTPDNS to send requests, set the HTTP Host header to the original domain name.

  2. If the HTTPDNS SDK returns an empty IP, fall back to the original domain name URL:

    String ip = DNSResolver.getInstance().getIPV4ByHost("your-domain");
    if (ip != null) {
    	// Replace the host in the URL with the IP address to make an API request.
    }else {
    	// Use the request URL of the original domain name to make a fallback request. 
        // In this case, use the original URL that contains the domain name to make a network request.
    }
  3. Download the demo program as a reference for integrating the HTTPDNS SDK.

  4. After integration, verify success on the Traffic Analysis page in the console. If no traffic appears, check that the Account ID, AccessKey ID, and AccessKey Secret are set correctly.

On-premises DNS

Starting from v2.3.0, the HTTPDNS Android SDK adds support for on-premises DNS deployment.

On-premises DNS suits scenarios requiring data compliance and custom resolution policies (finance, government, large enterprises). The SDK supports four deployment modes: public cloud only, on-premises only, and two primary-standby hybrids.

Core capabilities

  • On-premises deployment support: Supports configuring on-premises DNS service endpoints by using IPv4/IPv6 addresses or host domain names.

  • Two-way authentication: This mechanism signs requests with a customer-specific accessKeyId and accessKeySecret to ensure secure communication.

  • Circuit breaking and health checks: If an on-premises DNS node fails 3 or more consecutive times, circuit breaking is automatically triggered. Afterward, its availability is checked every minute by using the specified healthCheckDomain. The node is automatically re-enabled after it recovers.

  • Certificate validation control: Starting with version 2.3.1.beta, you can disable TLS certificate validation for testing on-premises DNS. Beta versions are for testing only and must not be used in production. Download beta version

  • Smart fallback: If the primary DNS (public cloud DNS or on-premises DNS) fails to resolve a domain name and the failure count reaches the specified threshold, the system automatically switches to the standby DNS to ensure high availability for resolution.

  • Seamless API compatibility: Whether you use public cloud DNS or on-premises DNS, the way you call the resolution API remains the same. You do not need to modify your business logic.

Configuration

1. Public cloud DNS only

This mode is for standard SaaS users who have not deployed an on-premises DNS.

DNSResolver.Init(this, accountID, accessKeyId, accessKeySecret);

2. On-premises DNS only

This mode is for customers who rely entirely on their on-premises DNS.

DNSResolver.InitFusionDNS(this,new String[]{"1.1.X.X","2.2.X.X"},null,null,"443", "check.example.com", "your_fusion_ak", "your_fusion_sk");
// Optional: Disable certificate validation (for test environments only, requires a beta SDK version, e.g., 2.3.1.beta).
// DNSResolver.setEnableCertificateValidation(false);

3. Primary: Public cloud, standby: On-premises

If the primary Alibaba Cloud public HTTPDNS fails to resolve a domain name, the system automatically falls back to the on-premises DNS.

// Primary: public cloud DNS
DNSResolver.Init(this, accountID, accessKeyId, accessKeySecret);
    
// Standby: on-premises DNS
DNSResolver.InitFusionDNS(this,new String[]{"1.1.X.X","2.2.X.X"},null,null,"443", "check.example.com", "your_fusion_ak", "your_fusion_sk");
// Optional: Disable certificate validation (for test environments only, requires a beta SDK version, e.g., 2.3.1.beta).
// DNSResolver.setEnableCertificateValidation(false);

4. Primary: On-premises, standby: Public cloud

Use the on-premises DNS as the primary DNS. If the on-premises DNS fails to resolve a domain name, the system automatically falls back to the Alibaba Cloud public cloud DNS.

// Primary: on-premises DNS
DNSResolver.InitFusionDNS(this,new String[]{"1.1.X.X","2.2.X.X"},null,null,"443", "check.example.com", "your_fusion_ak", "your_fusion_sk");
// Optional: Disable certificate validation (for test environments only, requires a beta SDK version, e.g., 2.3.1.beta).
// DNSResolver.setEnableCertificateValidation(false);

// Standby: public cloud DNS
DNSResolver.Init(this, accountID, accessKeyId, accessKeySecret);

New APIs

The SDK adds three core APIs for on-premises deployment and high-availability disaster recovery:

1. Configure endpoints and authentication

    /** For on-premises DNS used in on-premises deployments. You do not need to call this method if you use only public DNS.
     *
     * Sets the address and authentication information for the on-premises DNS server.
     * Customers use this interface to pass the address and authentication credentials of the private DNS server.
     * The SDK uses this information to initiate requests.
     * @param ctx The context.
     * @param serverIpv4Arr The array of IPv4 addresses (can be nil).
     * @param serverIpv6Arr The array of IPv6 addresses (can be nil).
     * @param serverHostArr The array of host domain names (can be nil).
     * @param port The service port, such as "443". If nil, the default port "443" is used.
     * @param healthCheckDomain The domain name that is used for health checks after circuit breaking is triggered. When a resolution service consecutively fails more than three times, circuit breaking is triggered, and the IP address of that service enters the healthCheck state. Subsequent requests will not be sent to this service. 
     *                          A timer runs every minute to call the resolution API by using this healthCheckDomain to probe whether the resolution service is available. If the probe is successful, the service returns to the alive state, and subsequent requests can be sent to this service.
     * @param accessKeyId The customer's private accessKeyId, which is used for authentication.
     * @param accessKeySecret The customer's private accessKeySecret, which is used for authentication.
     */
    public static void InitFusionDNS(Context ctx,String[] serverIpv4Arr, String[] serverIpv6Arr, String[] serverHostArr, String port, String healthCheckDomain, String accessKeyId, String accessKeySecret)

2. Control TLS certificate validation

TLS validation in beta versions

    /** For on-premises DNS used in on-premises deployments. You do not need to call this method if you use only public DNS.
     *
     * Specifies whether to enable certificate validation for on-premises DNS. Default value: true. If the server is not configured with a domain certificate or an IP certificate, you can set this parameter to false for testing. In production environments, we strongly recommend that you set this parameter to true. Otherwise, security risks may arise.
     * @param enable Set this parameter to true to enable certificate validation (default), or false to disable it.
     */
    public static void setEnableCertificateValidation(boolean enable)

3. Set the fallback threshold

    /** If you configure both public cloud DNS and on-premises DNS, you can specify the number of times that DNS resolution on the primary DNS must fail before the system automatically falls back to the standby DNS. If you configure only one type of DNS, you do not need to call this method.
     *
     * Specifies the number of times that DNS resolution on the primary DNS must fail before the system automatically falls back to the standby DNS. If you configure only one type of DNS, you do not need to call this method.
     * @param fallbackThreshold The failure threshold. Default: 4 if the primary DNS is public cloud DNS, or 2 if the primary DNS is on-premises DNS.
     * Valid range: [0, 4]. A value of 0 indicates an immediate fallback.
     */
     public static void setFallbackThreshold(int fallbackThreshold)