The Android SDK integration flow covers Android SDK import, configuration, IP resolution, and network library integration. This topic describes how to integrate HTTPDNS in Android WebView using OkHttp.
1. Background
Alibaba Cloud HTTPDNS helps prevent DNS hijacking by bypassing system DNS resolution. You call the HTTPDNS API to resolve domain names, which reduces or eliminates hijacking risks.
In WebView, you can intercept web page network requests and route them through the native layer (App). During this process, HTTPDNS resolution replaces system DNS to prevent hijacking.
This topic focuses on OkHttp, the mainstream Android network library.
-
This topic provides a reference solution. The code samples are for reference only and are not intended for direct use in production. Evaluate carefully before implementing.
-
Android systems vary by vendor. Test this solution in a non-production environment first. For questions, contact Technical support.
-
This topic covers HTTPDNS in WebView scenarios only. For HTTPDNS resolution fundamentals, review Android SDK integration.
2. GET-only interception: limitations and impact
Android's WebViewClient.shouldInterceptRequest() method provides details only for GET requests. For POST and other methods, the request body is unavailable, making full HTTPDNS interception impossible.
GET-only interception still provides significant value:
-
Static resources use GET requests. Web page resources (images, scripts, stylesheets) load through GET requests. Intercepting these with HTTPDNS covers most resource loading and significantly reduces hijacking risk.
-
Many API calls use GET. Query and cacheable API endpoints typically use GET requests, which HTTPDNS interception also protects.
-
HTTPDNS improves CDN routing accuracy. CDN performance depends on accurate DNS resolution. HTTPDNS bypasses system DNS hijacking and ensures resolution points to the optimal CDN node, improving access speed.
-
GET interception covers most hijacking scenarios. DNS hijacking typically targets popular and static resource domains, which are almost exclusively accessed through GET requests. GET-only HTTPDNS covers the vast majority of traffic, achieving 80% or higher protection.
Although only GET requests can be intercepted in WebView, this covers the majority of traffic and enables more accurate CDN routing, significantly improving both security and performance.
3. Demo code
HTTPDNS+WebView+OkHttp best practices sample code is in the WebView+HTTPDNS+OkHttp Android Demo.
4. Implementation
The following steps describe how to integrate HTTPDNS with OkHttp and WebView to intercept and process web page requests in the native layer.
4.1 OkHttp configuration
4.1.1 Custom DNS resolution
OkHttp's Dns interface lets you customize DNS resolution. Integrate HTTPDNS through this interface:
OkHttpClient.Builder()
// Customize the DNS resolution logic.
.dns(object : Dns {
override fun lookup(hostname: String): List<InetAddress> {
val inetAddresses = mutableListOf<InetAddress>()
HttpDns.getService(accountId)
.getHttpDnsResultForHostSyncNonBlocking(hostname, RequestIpType.auto)?.apply {
if (!ipv6s.isNullOrEmpty()) {
for (i in ipv6s.indices) {
inetAddresses.addAll(
InetAddress.getAllByName(ipv6s[i]).toList()
)
}
} else if (!ips.isNullOrEmpty()) {
for (i in ips.indices) {
inetAddresses.addAll(
InetAddress.getAllByName(ips[i]).toList()
)
}
}
}
if (inetAddresses.isEmpty()) {
inetAddresses.addAll(Dns.SYSTEM.lookup(hostname))
}
return inetAddresses
}
})
.build()
new OkHttpClient.Builder()
// Customize DNS resolution logic.
.dns(new Dns() {
@NonNull
@Override
public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
ArrayList<InetAddress> inetAddresses = new ArrayList<>();
HTTPDNSResult result = HttpDns.getService(accountiD)
.getHttpDnsResultForHostSync(hostname, RequestIpType.auto);
if (result != null) {
if (result.getIpv6s() != null && result.getIpv6s().length > 0) {
for (int i = 0; i < result.getIpv6s().length; i++) {
InetAddress[] ipV6InetAddresses = InetAddress.getAllByName(result.getIpv6s()[i]);
inetAddresses.addAll(Arrays.asList(ipV6InetAddresses));
}
} else if (result.getIps() != null && result.getIps().length > 0) {
for (int i = 0; i < result.getIps().length; i++) {
InetAddress[] ipV4InetAddresses = InetAddress.getAllByName(result.getIps()[i]);
inetAddresses.addAll(Arrays.asList(ipV4InetAddresses));
}
}
}
if (inetAddresses.isEmpty()) {
inetAddresses.addAll(Dns.SYSTEM.lookup(hostname));
}
return inetAddresses;
}
})
.build();Fall back to local DNS when HTTPDNS resolution fails.
4.1.2 Disable redirection
This solution delegates redirection to WebView's default handler instead of the network layer, preventing cookie and resource loading errors in redirect scenarios:
OkHttpClient.Builder()
.followRedirects(false) // Disable HTTP redirection.
.followSslRedirects(false) // Disable HTTPS redirection.
.build()new OkHttpClient.Builder()
.followRedirects(false) // Disable HTTP redirection.
.followSslRedirects(false) // Disable HTTPS redirection.
.build();In untrusted network environments, redirected requests can still be tampered with. Implement security protection on the client side.
4.2 WebView intercepts network requests
4.2.1 Implement WebViewClient
WebView's WebViewClient interface lets you intercept resource loading by overriding shouldInterceptRequest(). This routes web page requests through the native layer (OkHttp) and returns results to WebView:
webview.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView?,
request: WebResourceRequest?
): WebResourceResponse? {
if (shouldIntercept(request)) {
return getResponseByOkHttp(request)
}
return super.shouldInterceptRequest(view, request)
}
}webview.setWebViewClient(new WebViewClient(){
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
if (shouldIntercept(request)) {
return getResponseByOkHttp(request);
}
return super.shouldInterceptRequest(view, request);
}
});4.2.2 Determine whether to intercept requests
The shouldIntercept() method intercepts requests only when:
-
The protocol is
httporhttps. Other protocols are handled byWebViewdirectly. -
The method is
GET.
Sample code:
private fun shouldIntercept(webResourceRequest: WebResourceRequest?): Boolean {
if (webResourceRequest == null) {
return false
}
val url = webResourceRequest.url ?: return false
// Non-HTTP requests are not intercepted.
if ("https" != url.scheme && "http" != url.scheme) {
return false
}
// Only GET requests are intercepted.
if ("GET".equals(webResourceRequest.method, true)) {
return true
}
return false
}private boolean shouldIntercept(WebResourceRequest request) {
if (request == null || request.getUrl() == null) {
return false;
}
// Non-HTTP requests are not intercepted.
if (!"http".equals(request.getUrl().getScheme()) && !"https".equals(request.getUrl().getScheme())) {
return false;
}
// Only GET requests are intercepted.
if ("GET".equalsIgnoreCase(request.getMethod())) {
return true;
}
return false;
}4.2.3 Use OkHttp for network requests
-
Use OkHttp to send network requests using the request URL, headers, and other information.
-
Wrap the response in a
WebResourceResponseand return it to WebView.
Sample code:
private fun getResponseByOkHttp(webResourceRequest: WebResourceRequest?): WebResourceResponse? {
if (webResourceRequest == null) {return null}
try {
val url = webResourceRequest.url.toString()
val requestBuilder =
Request.Builder().url(url).method(webResourceRequest.method, null)
val requestHeaders = webResourceRequest.requestHeaders
if (!requestHeaders.isNullOrEmpty()) {
requestHeaders.forEach {
requestBuilder.addHeader(it.key, it.value)
}
}
val response = okHttpClient.newCall(requestBuilder.build()).execute()
val code = response.code
if (code != 200) {
return null
}
val body = response.body
if (body != null) {
val contentType = body.contentType()
val encoding = contentType?.charset()
val mediaType = contentType?.toString()
var mimeType = "text/plain"
if (!TextUtils.isEmpty(mediaType)) {
val mediaTypeElements = mediaType?.split(";")
if (!mediaTypeElements.isNullOrEmpty()) {
mimeType = mediaTypeElements[0]
}
}
val responseHeaders = mutableMapOf<String, String>()
for (header in response.headers) {
responseHeaders[header.first] = header.second
}
var message = response.message
if (message.isBlank()) {
message = "OK"
}
val resourceResponse =
WebResourceResponse(mimeType, encoding?.name(), body.byteStream())
resourceResponse.responseHeaders = responseHeaders
resourceResponse.setStatusCodeAndReasonPhrase(code, message)
return resourceResponse
}
} catch (e: Throwable) {
e.printStackTrace()
}
return null
}private WebResourceResponse getResponseByOkHttp(WebResourceRequest request) {
try {
String url = request.getUrl().toString();
Request.Builder requestBuilder = new Request.Builder()
.url(url)
.method(request.getMethod(), null);
Map<String, String> requestHeaders = request.getRequestHeaders();
if (requestHeaders != null) {
for (Map.Entry<String, String> entry : requestHeaders.entrySet()) {
requestBuilder.addHeader(entry.getKey(), entry.getValue());
}
}
Response response = okHttpClient.newCall(requestBuilder.build()).execute();
if (200 != response.code()) {
return null;
}
ResponseBody body = response.body();
if (body != null) {
MediaType contentType = body.contentType();
if (contentType != null) {
Charset encoding = contentType.charset();
String mediaType = contentType.toString();
String mimeType = "text/plain";
if (!TextUtils.isEmpty(mediaType)) {
String[] mediaTypeElements = mediaType.split(";");
if (mediaTypeElements.length > 0) {
mimeType = mediaTypeElements[0];
}
}
Map<String, String> responseHeaders = new HashMap<>();
for (String key : response.headers().names()) {
responseHeaders.put(key, response.header(key));
}
String message = response.message();
if (TextUtils.isEmpty(message)) {
message = "OK";
}
WebResourceResponse resourceResponse = new WebResourceResponse(mimeType, encoding.name(), body.byteStream());
resourceResponse.setResponseHeaders(responseHeaders);
resourceResponse.setStatusCodeAndReasonPhrase(response.code(), message);
return resourceResponse;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}5. Verification
After integration, follow Verify a successful network library integration to test with hijack impersonation or fault injection testing.
6. Summary
-
HTTPDNS reduces DNS hijacking risk. Intercepting WebView requests through OkHttp with HTTPDNS significantly improves DNS resolution security.
-
WebView limits interception to GET requests, but this covers most scenarios including static resource loading and common GET API calls.
-
Test before production. Deploy this solution in a test environment first, monitor for exceptions, and customize as needed for your business requirements.
For questions or technical support, contact Technical support.