1page.title=Migrating to WebView in Android 4.4
2@jd:body
3
4<div id="qv-wrapper">
5<div id="qv">
6
7<h2>In this document</h2>
8<ol>
9  <li><a href="#UserAgent">User Agent Changes</a></li>
10  <li><a href="#Threads">Multi-threading and Thread Blocking</a></li>
11  <li><a href="#URLs">Custom URL Handling</a></li>
12  <li><a href="#Viewport">Viewport Changes</a>
13    <ol>
14      <li><a href="#TargetDensity">Viewport target-densitydpi no longer supported</a></li>
15      <li><a href="#SmallViewport">Viewport zooms in when small</a></li>
16      <li><a href="#MultiViewport">Multiple viewport tags not supported</a></li>
17      <li><a href="#Zoom">Default zoom is deprecated</a></li>
18    </ol>
19  </li>
20  <li><a href="#Style">Styling Changes</a>
21    <ol>
22      <li><a href="#BackgroundSize">The background CSS shorthand overrides background-size</a></li>
23      <li><a href="#Pixels">Sizes are in CSS pixels instead of screen pixels</a></li>
24      <li><a href="#Columns">NARROW_COLUMNS and SINGLE_COLUMN no longer supported</a></li>
25    </ol>
26  </li>
27  <li><a href="#TouchCancel">Handling Touch Events in JavaScript</a></li>
28</ol>
29
30</div>
31</div>
32
33
34
35
36
37
38
39
40
41
42<p>Android 4.4 (API level 19) introduces a new version of {@link android.webkit.WebView} that is
43based on <a href="http://www.chromium.org/Home">Chromium</a>. This change upgrades
44{@link android.webkit.WebView} performance and standards support for HTML5, CSS3, and JavaScript
45to match the latest web browsers. Any apps using {@link android.webkit.WebView} will inherit these
46upgrades when running on Android 4.4 and higher.</p>
47
48<p>This document describes additional changes
49to {@link android.webkit.WebView} that you should be aware of if you set your
50<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
51targetSdkVersion}</a> to "19" or higher.</p>
52
53<p class="note"><strong>Note:</strong>
54If your <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
55targetSdkVersion}</a> is set to "18" or lower, {@link android.webkit.WebView} operates in
56"quirks mode" in order to avoid some of the behavior changes described below, as closely
57as possible&mdash;while still providing your app the performance and web standards upgrades.
58Beware, though, that <a href="#Columns">single and narrow column layouts</a> and <a
59href="#Zoom">default zoom levels</a> are
60<strong>not supported at all</strong> on Android 4.4, and there may be other behavioral differences
61that have not been identified, so be sure to test your app on Android 4.4
62or higher even if you keep your <a
63href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
64targetSdkVersion}</a> set to "18" or lower. </p>
65
66<p>To help you work through any issues you may encounter when migrating your app to
67{@link android.webkit.WebView} in Android 4.4, you can enable remote debugging through
68Chrome on your desktop by calling
69{@link android.webkit.WebView#setWebContentsDebuggingEnabled setWebContentsDebuggingEnabled()}.
70This new feature in {@link android.webkit.WebView} allows
71you to inspect and analyze your web content, scripts, and network activity while running in
72a {@link android.webkit.WebView}. For more information, see <a
73href="https://developers.google.com/chrome-developer-tools/docs/remote-debugging">Remote
74Debugging on Android</a>.</p>
75
76
77
78
79<h2 id="UserAgent">User Agent Changes</h2>
80
81<p>If you serve content to your {@link android.webkit.WebView} based on the user agent, you should
82to be aware of the user agent string has changed slightly and now includes the Chrome version:</p>
83
84<pre class="no-pretty-print">
85Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16H) AppleWebKit/537.36
86(KHTML, like Gecko) Version/4.0 <strong>Chrome/30.0.0.0</strong> Mobile Safari/537.36
87</pre>
88
89<p>If you need to retrieve the user agent but don't need to store it for your app or
90do not want to instantiate {@link android.webkit.WebView}, you should use
91the static method, {@link android.webkit.WebSettings#getDefaultUserAgent
92getDefaultUserAgent()}. However, if you intend to override the user agent string in your
93{@link android.webkit.WebView}, you may instead want to use
94{@link android.webkit.WebSettings#getUserAgentString getUserAgentString()}.</p>
95
96
97
98
99<h2 id="Threads">Multi-threading and Thread Blocking</h2>
100
101<p>If you call methods on {@link android.webkit.WebView} from any thread other than your app's
102UI thread, it can cause unexpected results. For example, if your app uses multiple threads,
103you can use the {@link android.app.Activity#runOnUiThread runOnUiThread()} method
104to ensure your code executes on the UI thread:</p>
105
106<pre>
107runOnUiThread(new Runnable() {
108    &#64;Override
109    public void run() {
110        // Code for WebView goes here
111    }
112});
113</pre>
114
115<p>Also be sure that you <a href="{@docRoot}guide/components/processes-and-threads.html#Threads">
116never block the UI thread</a>. A situation in which some apps make this mistake is while waiting for
117a JavaScript callback. For example, <strong>do not</strong> use code like this:</p>
118
119<pre>
120// This code is BAD and will block the UI thread
121webView.loadUrl("javascript:fn()");
122while(result == null) {
123  Thread.sleep(100);
124}
125</pre>
126
127<p>You can instead use a new method, {@link android.webkit.WebView#evaluateJavascript
128evaluateJavascript()}, to run JavaScript asynchronously.</p>
129
130
131
132
133
134<h2 id="URLs">Custom URL Handling</h2>
135
136<p>The new {@link android.webkit.WebView} applies additional restrictions when requesting resources
137and resolving links that use a custom URL scheme. For example, if you implement callbacks such as
138{@link android.webkit.WebViewClient#shouldOverrideUrlLoading shouldOverrideUrlLoading()} or
139{@link android.webkit.WebViewClient#shouldInterceptRequest shouldInterceptRequest()}, then
140{@link android.webkit.WebView} invokes them only for valid URLs.</p>
141
142<p>If you are using a custom URL scheme or a base URL and
143notice that your app is receiving fewer calls to these callbacks or failing
144to load resources on Android 4.4, ensure that the requests specify valid URLs that conform to
145<a href="http://tools.ietf.org/html/rfc3986#appendix-A">RFC 3986</a>.
146
147<p>For example, the new {@link android.webkit.WebView} may not call your
148{@link android.webkit.WebViewClient#shouldOverrideUrlLoading shouldOverrideUrlLoading()} method
149for links like this:</p>
150
151<pre>&lt;a href="showProfile">Show Profile&lt;/a></pre>
152
153<p>The result of the user clicking such a link can vary:
154<ul>
155  <li>If you loaded the page by calling {@link android.webkit.WebView#loadData
156loadData()} or {@link android.webkit.WebView#loadDataWithBaseURL
157loadDataWithBaseURL()} with an invalid or null base URL, then you will not receive the
158{@link android.webkit.WebViewClient#shouldOverrideUrlLoading shouldOverrideUrlLoading()} callback
159for this type of link on the page.
160  <p class="note"><strong>Note:</strong>
161  When you use {@link android.webkit.WebView#loadDataWithBaseURL
162loadDataWithBaseURL()} and the base URL is invalid or set null, all links in the content
163you are loading must be absolute.</p>
164  <li>If you loaded the page by calling {@link android.webkit.WebView#loadUrl
165loadUrl()} or provided a valid base URL with {@link android.webkit.WebView#loadDataWithBaseURL
166loadDataWithBaseURL()}, then you will receive the
167{@link android.webkit.WebViewClient#shouldOverrideUrlLoading shouldOverrideUrlLoading()} callback
168for this type of link on the page, but the URL you receive will be absolute, relative
169to the current page. For example, the URL you receive will be
170<code>"http://www.example.com/showProfile"</code> instead of just <code>"showProfile"</code>.
171</ul>
172
173
174<p>Instead of using a simple string in a link as shown above, you can use a custom scheme such
175as the following:</p>
176
177<pre>&lt;a href="example-app:showProfile">Show Profile&lt;/a></pre>
178
179<p>You can then handle this URL in your
180{@link android.webkit.WebViewClient#shouldOverrideUrlLoading shouldOverrideUrlLoading()} method
181like this:</p>
182
183<pre>
184// The URL scheme should be non-hierarchical (no trailing slashes)
185private static final String APP_SCHEME = "example-app:";
186
187&#64;Override
188public boolean shouldOverrideUrlLoading(WebView view, String url) {
189    if (url.startsWith(APP_SCHEME)) {
190        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
191        respondToData(urlData);
192        return true;
193    }
194    return false;
195}
196</pre>
197
198<p>If you can't alter the HTML then you may be able to use
199{@link android.webkit.WebView#loadDataWithBaseURL loadDataWithBaseURL()} and set a base URL
200consisting of a custom scheme and a valid host, such as
201<code>"example-app://&lt;valid_host_name>/"</code>. For example:</p>
202
203<pre>
204webView.loadDataWithBaseURL("example-app://example.co.uk/", HTML_DATA,
205        null, "UTF-8", null);
206</pre>
207
208<p>The valid host name should conform to
209<a href="http://tools.ietf.org/html/rfc3986#appendix-A">RFC 3986</a>
210and it's important to include the trailing slash at the end, otherwise, any requests from the
211loaded page may be dropped.</p>
212
213
214
215<h2 id="Viewport">Viewport Changes</h2>
216
217
218<h3 id="TargetDensity">Viewport target-densitydpi no longer supported</h3>
219
220<p>Previously, {@link android.webkit.WebView} supported a viewport property called
221<code>target-densitydpi</code> to help web pages specify their intended screen density. This
222property is no longer supported and you should migrate to using standard solutions with
223images and CSS as discussed in <a
224href="http://developers.google.com/chrome/mobile/docs/webview/pixelperfect">Pixel-Perfect UI in
225the WebView</a>.</p>
226
227
228<h3 id="SmallViewport">Viewport zooms in when small</h3>
229
230<p>Previously, if you set your viewport width to a value less than or equal to "320"
231it would be set to "device-width", and if you set the viewport height to a value less than or
232equal to the {@link android.webkit.WebView} height, it would be set to "device-height". However,
233when running in the new {@link android.webkit.WebView}, the width or height value is adhered and
234the {@link android.webkit.WebView} zooms in to fill the screen width.</p>
235
236
237<h3 id="MultiViewport">Multiple viewport tags not supported</h3>
238
239<p>Previously, if you included multiple viewport tags in a web page, {@link android.webkit.WebView}
240would merge the properties from all the tags.
241In the new {@link android.webkit.WebView}, only the last viewport is
242used and all others are ignored.</p>
243
244
245<h3 id="Zoom">Default zoom is deprecated</h3>
246
247<p>The methods {@link android.webkit.WebSettings#getDefaultZoom()} and
248{@link android.webkit.WebSettings#setDefaultZoom setDefaultZoom()} for getting and setting
249the initial zoom level on a page have are no longer supported and you should instead define
250the appropriate viewport in the web page.</p>
251
252<p class="caution"><strong>Caution:</strong> These APIs are not supported on Android 4.4 and higher
253at all. Even if your <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target"
254>{@code targetSdkVersion}</a> is set to "18" or lower, these APIs have no effect.</p>
255
256<p>For information about how to define the viewport properties in your HTML, read
257<a href="http://developers.google.com/chrome/mobile/docs/webview/pixelperfect" class="external-link"
258>Pixel-Perfect UI in the WebView</a>.
259
260<p>If you cannot set the width of the viewport in the HTML, then you should call
261{@link android.webkit.WebSettings#setUseWideViewPort setUseWideViewPort()} to ensure the page
262is given a larger viewport. For example:</p>
263
264<pre>
265WebSettings settings = webView.getSettings();
266settings.setUseWideViewPort(true);
267settings.setLoadWithOverviewMode(true);
268</pre>
269
270
271
272
273
274<h2 id="Style">Styling Changes</h2>
275
276<h3 id="BackgroundSize">The background CSS shorthand overrides background-size</h3>
277
278<p>Chrome and other browser have behaved this way for a while, but now
279{@link android.webkit.WebView} will also override a CSS setting for {@code background-size}
280if you also specify the {@code background} style. For example, the size here will be reset
281to a default value:</p>
282
283<pre class="no-pretty-print">
284.some-class {
285  background-size: contain;
286  background: url('images/image.png') no-repeat;
287}
288</pre>
289
290<p>The fix is to simply switch the two properties around.</p>
291
292<pre class="no-pretty-print">
293.some-class {
294  background: url('images/image.png') no-repeat;
295  background-size: contain;
296}
297</pre>
298
299
300<h3 id="Pixels">Sizes are in CSS pixels instead of screen pixels</h3>
301
302<p>Previously, size parameters such as <a
303href="https://developer.mozilla.org/en-US/docs/Web/API/Window.outerWidth" class="external-link">
304<code>window.outerWidth</code></a> and
305<a href="https://developer.mozilla.org/en-US/docs/Web/API/Window.outerHeight"
306class="external-link"><code>window.outerHeight</code></a> returned a value in actual screen pixels.
307In the new {@link android.webkit.WebView}, these return a value based on CSS pixels.</p>
308
309<p>It's generally bad practice to try and calculate the physical size in pixels for
310sizing elements or other calculations. However, if you've disabled zooming and the initial-scale
311is set to 1.0, you can use <code>window.devicePixelRatio</code>
312to get the scale, then multiply the CSS pixel value by that. Instead,
313you can also <a href="{@docRoot}guide/webapps/webview.html#BindingJavaScript">create a
314JavaScript binding</a> to query the pixel size from the {@link android.webkit.WebView} itself.</p>
315
316<p>For more information, see <a class="external-link"
317href="http://www.quirksmode.org/blog/archives/2012/03/windowouterwidt.html">quirksmode.org</a>.</p>
318
319
320
321<h3 id="Columns">NARROW_COLUMNS and SINGLE_COLUMN no longer supported</h3>
322
323<p>The {@link android.webkit.WebSettings.LayoutAlgorithm#NARROW_COLUMNS} value for {@link
324android.webkit.WebSettings.LayoutAlgorithm} is not be supported in the new {@link
325android.webkit.WebView}.</p>
326
327<p class="caution"><strong>Caution:</strong> These APIs are not supported on Android 4.4 and higher
328at all. Even if your <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target"
329>{@code targetSdkVersion}</a> is set to "18" or lower, these APIs have no effect.</p>
330
331<p>You can handle this change in the following ways:</p>
332
333<ul>
334  <li>Alter the styles of your application:
335    <p>If you have control of the HTML and CSS on the page, you may find that altering the design
336    of your content may be the most reliable approach. For example, for screens where you cite
337    licenses, you may want wrap text inside of a
338    <code>&lt;pre></code> tag, which you could do with the following styles:
339    <pre>&lt;pre style="word-wrap: break-word; white-space: pre-wrap;"></pre>
340    <p>This may be especially helpful if you have not defined the viewport properties for
341    your page.</p>
342  </li>
343  <li>Use the new {@link android.webkit.WebSettings.LayoutAlgorithm#TEXT_AUTOSIZING} layout
344  algorithm:
345    <p>If you were using narrow columns as a way to make a broad spectrum of desktop
346    sites more readable on mobile devices and you aren't able to change the HTML content, the new
347    {@link android.webkit.WebSettings.LayoutAlgorithm#TEXT_AUTOSIZING} algorithm may be a
348    suitable alternative to {@link android.webkit.WebSettings.LayoutAlgorithm#NARROW_COLUMNS}.</p>
349  </li>
350</ul>
351
352<p>Additionally, the {@link android.webkit.WebSettings.LayoutAlgorithm#SINGLE_COLUMN} value&mdash;which
353was previously deprecated&mdash;is also not supported in the new {@link
354android.webkit.WebView}.</p>
355
356
357
358
359<h2 id="TouchCancel">Handling Touch Events in JavaScript</h2>
360
361<p>If your web page is directly handling touch events in a {@link android.webkit.WebView},
362be sure you are also handling the <a
363href="https://developer.mozilla.org/en-US/docs/Web/Reference/Events/touchcancel"
364class="external-link"><code>touchcancel</code></a>
365event. There are a few scenarios where <code>touchcancel</code> will be called, which can
366cause problems if not received:</p>
367
368<ul>
369  <li>An element is touched (so <code>touchstart</code> and <code>touchmove</code> are called)
370  and the page is scrolled, causing a <code>touchcancel</code> to be thrown.</li>
371  <li>An element is touched (<code>touchstart</code> is called) but
372  <code>event.preventDefault()</code> is not called, resulting earlier enough that
373  <code>touchcancel</code> is thrown (so
374  {@link android.webkit.WebView} assumes you don't want to consume the touch events).</li>
375</ul>
376
377
378
379