Intro
One of the neat things about WebView on Android is the level of control that you are given over the WebView component. In addition to controlling settings and navigating to URLs, you can actually control the WebView at the network request level, intercepting network calls, modifying content, or even blocking requests altogether.
This post is about how to implement some network request blocking (and modification) with WebView and related classes, such as WebViewClient
. Examples will focus on Kotlin, but would apply to Java as well with just a little tweaking.
Where to Hook into WebView Requests
There are a lot of different ways to customize your WebView within Android code; settings, callbacks, WebChromeClient, and WebViewClient.
For those wondering what the difference is between
WebChromeClient
andWebViewClient
, the answer is mostly in separation of concerns. The WebChromeClient class is mostly concerned with long-lived browser-level things, such as the user’s visit history, whereas WebViewClient has more to do with the lifecycle of each individual webpage and things like hooking into when a page has finished loading.
For hooking into network requests, the easiest entry point is by subclassing WebViewClient and overriding two important functions:
These methods both do similar things; they let you, as a developer, intercept and modify how each browser request is handled.
The difference is a little subtle between these methods; although AJAX requests use URLs, the
shouldOverrideUrlLoading
is really only called for the URL of the webpage itself – e.g. the one that the user types in. AJAX and other requests will not come through this hook, but will flow throughshouldInterceptRequest
.
Writing our WebView Request Modifiers
Since we will want to use both hooks (shouldOverrideUrlLoading
and shouldInterceptRequest
), and both have identical method signatures, this is an ideal situation to keep our code clean and have a single function that can be called by both:
fun filterNetworkRequests(view: WebView, request: WebResourceRequest): Boolean {
// By default, let all requests pass through
var shouldBlock = false;
// ....
// code to determine if shouldBlock should be changed
return shouldBlock
}
Although we can call this function from both hooks with identical arguments, how we use the response actually changes in each.
shouldOverrideUrlLoading
: Returningtrue
will abort / block the URL being loaded,false
allows it to proceed as normalshouldInterceptRequest
: To block the request, we need to actually return our own custom response which should replace the actual one. Returningnull
will allow it to proceed as normal.
Knowing this, here is how we could use our common function within each hook:
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest
): WebResourceResponse? {
val shouldBlock = filterNetworkRequests(webView, request);
if (shouldBlock) {
return WebResourceResponse("text/javascript", "UTF-8", null)
}
return null
}
override fun shouldOverrideUrlLoading(
view: WebView,
request: WebResourceRequest
): Boolean {
return filterNetworkRequests(view, request)
}
Implementing Blocking Patterns
To take this a step further, I’m going to demonstrate how you could use these hooks with blocklists to prevent any requests from being fulfilled for certain domains or URL patterns.
First, let’s declare our block lists:
val BannedDomains: Array<String> = arrayOf(
"facebook.com",
"connect.facebook.net"
)
val BannedUrlPatterns: Array<Regex> = arrayOf(
Regex("\\.taboola\\.")
)
Next, we can modify our reusable filterNetworkRequests function to take these into account:
fun filterNetworkRequests(view: WebView, request: WebResourceRequest): Boolean {
// By default, let all requests pass through
var shouldBlock = false;
val reqUri = request.url
val reqUrl = reqUri.toString()
if (BannedDomains.contains(reqUri.host?.replace("www.", ""))) {
shouldBlock = true
}
for (bannedPattern in BannedUrlPatterns) {
if (!shouldBlock) {
shouldBlock = bannedPattern.containsMatchIn(reqUrl)
}
}
return shouldBlock
}
Request Blocking in WebView: A Complete Demo
Since sometimes it helps to see everything put together, here is a complete (although basic) working demo of request blocking with WebView, using the previous steps we built out.
Loading Github Gist: https://gist.github.com/joshuatz/e801edbe9f545ebddb2352f2d1941db3