OkHttp as HTTP Engine for WebView

OkHttp as HTTP Engine for WebView
Statue of Melpomene Muse of Tragedy, 2nd century AD.

DAMN DAMN DAMN IT WEBVIEW.
That was a bad idea, don't do it, see "why" post
http://artemzin.com/blog/android-webview-io/

If you have a WebView in your Android app you may want to intercept its requests to handle them as you want, and for example load resources via OkHttp.

NOTICE: this is mostly suitable only for cases when you use WebView just for displaying information (only GET requests, because on API < 21 you can not resolve method of the request GET, POST, etc), not for complex js applications running inside of your WebView.

Reasons to switch to OkHttp for loading WebView resources:
  • You can use latests version of OkHttp with support for modern protocols like SPDY and HTTP/2! (BTW, we've boosted our app because Nginx on our CDNs supports SPDY!)
  • With Stetho and OkHttp-Stetho-Interceptor you can easily inspect loading of resources in WebView even on devices without support of WebView debugging!
  • With OkHttp you will use its connection pool and if you usually load resources from same endpoints that you use as your REST/etc APIs — you'll get a performance boost on reusing same connection.
How to do it?

Basically you need to:

  1. Attach WebViewClient to the WebView
  2. In your WebViewClient you should override shouldInterceptRequest(WebView webView, String url) and shouldInterceptRequest(WebView webView, WebResourceRequest request) (this method is for API >= 21).
  3. Handle requests via OkHttp

Intercept requests on API < 21

@SuppressWarnings("deprecation") // From API 21 we should use another overload
@Override
public WebResourceResponse shouldInterceptRequest(@NonNull WebView view, @NonNull String url) {
  if (urlShouldBeHandledByWebView(url)) {
    return super.shouldInterceptRequest(view, url);
  }

  return handleRequestViaOkHttp(url);
}

Intercept requests on API >= 21

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(@NonNull WebView view, @NonNull WebResourceRequest request) {
  if (urlShouldBeHandledByWebView(url)) {
    return super.shouldInterceptRequest(view, request);
  }

  return handleRequestViaOkHttp(url);
}

Decide what requests should be intercepted:

static boolean urlShouldBeHandledByWebView(@NonNull String url) {
  // file: Resolve requests to local files such as files from cache folder via WebView itself
  return url.startsWith("file:");
}

And finally, execute request via OkHttp

@NonNull
private WebResourceResponse handleRequestViaOkHttp(@NonNull String url) {
  try {
    // On Android API >= 21 you can get request method and headers
    // As I said, we need to only display "simple" page with resources
    // So it's GET without special headers
    final Call call = okHttpClient.newCall(new Request.Builder()
        .url(url)
        .build()
    );

    final Response response = call.execute();

    return new WebResourceResponse(
      response.header("content-type", "text/plain"), // You can set something other as default content-type
      response.header("content-encoding", "utf-8"),  // Again, you can set another encoding as default
      response.body().byteStream()
    );
  } catch (Exception e) {
    return ...; // return response for bad request
  }
}