Background
We often work with hybrid Android apps that have both native code (written in Java) and web code (written in HTML /JavaScript). When we do, we often need a quick way to test whether the HTML component works properly. Typically, this means loading the HTML in a mobile browser. If the HTML loads successfully and renders correctly, then the HTML should also work in the app’s webview.
When we do this kind of testing, we try to make sure that the web browser employed for testing uses the same HTML redering engine as the webview. On Android versions up to JellyBean, this means using the classic Android browser, which according to Google shares the same rendering engine as the webview. On JellyBean though, with Google’s introduction of the Chrome browser and with some talk about a Chrome-based webview, things got a bit murky… Does the JellyBean webview use the Android Browser or the Google Chrome rendering engine? Which browser should you use to test your HTML on JellyBean?
The Search
To get a definitive answer to this question we are going to dive into the Android codebase. For that, we will turn to GrepCode, a site that lets anyone quickly browse multiple versions of the Android OS codebase without needing to download the multi-gigabyte source code repositories.
We begin our search in the source code for Android 4.2.2 r1, which can be found here.
Since we’re interested in the WebView class, let’s find that class in the source code. We know from the Android documentation that the WebView class is located in the android.webkit package. You can browse to that package and open the WebView.java file.
Any Java object is instantiated using its constructor, so let’s start there. There are several chained constructors in the WebView source code. All of them eventually call this constructor at line 501:
@SuppressWarnings("deprecation") // for super() call into deprecated base class constructor. protected WebView(Context context, AttributeSet attrs, int defStyle, Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) { super(context, attrs, defStyle); if (context == null) { throw new IllegalArgumentException("Invalid context argument"); } checkThread(); ensureProviderCreated(); mProvider.init(javaScriptInterfaces, privateBrowsing); }
The checkThread function is not too interesting, so let’s look at the next one called ensureProviderCreated:
private void ensureProviderCreated() { checkThread(); if (mProvider == null) { // As this can get called during the base class constructor chain, pass the minimum // number of dependencies here; the rest are deferred to init(). mProvider = getFactory().createWebView(this, new PrivateAccess()); } }
Based on the code above, the provider is generated using a Factory pattern. The returned WebViewFactoryProvider class is instantiated in the getFactory function:
private static synchronized WebViewFactoryProvider getFactory() { // For now the main purpose of this function (and the factory abstration) is to keep // us honest and minimize usage of WebViewClassic internals when binding the proxy. checkThread(); return WebViewFactory.getProvider(); }
Notice that most of the work in the function above is done in a static function called WebViewFactory.getProvider. Clicking on getProvider in GrepCode will jump us the WebViewFactory.java file. Within that function, at line 52, we see:
// For debug builds, we allow a system property to specify that we should use the // Chromium powered WebView. This enables us to switch between implementations // at runtime. For user (release) builds, don't allow this. if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("webview.use_chromium", false)) { StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); try { sProviderInstance = loadChromiumProvider(); if (DEBUG) Log.v(LOGTAG, "Loaded Chromium provider: " + sProviderInstance); } finally { StrictMode.setThreadPolicy(oldPolicy); } } if (sProviderInstance == null) { if (DEBUG) Log.v(LOGTAG, "Falling back to default provider: " + DEFAULT_WEBVIEW_FACTORY); sProviderInstance = getFactoryByName(DEFAULT_WEBVIEW_FACTORY, WebViewFactory.class.getClassLoader()); if (sProviderInstance == null) { if (DEBUG) Log.v(LOGTAG, "Falling back to explicit linkage"); sProviderInstance = new WebViewClassic.Factory(); } }
Within the same file, at line 32, we have:
private static final String DEFAULT_WEBVIEW_FACTORY = "android.webkit.WebViewClassic$Factory"; private static final String CHROMIUM_WEBVIEW_FACTORY = "com.android.webviewchromium.WebViewChromiumFactoryProvider";
As you can see from these last two code snippets, the default WebView provider is still the “classic” webview that is based on the Android Browser rendering engine. Apparently there is work underway to switch the webview to the Chrome rendering engine, but right now that’s only available in debug Android builds using a special flag.
Conclusion
Based on an examination of the Android source code, the webview does not use the Chrome rendering engine in Android 4.2 (JellyBean). The webview is still powered by the Android Browser rendering engine.