Home | History | Annotate | Download | only in webapps
      1 page.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
     43 based 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
     45 to match the latest web browsers. Any apps using {@link android.webkit.WebView} will inherit these
     46 upgrades when running on Android 4.4 and higher.</p>
     47 
     48 <p>This document describes additional changes
     49 to {@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
     51 targetSdkVersion}</a> to "19" or higher.</p>
     52 
     53 <p class="note"><strong>Note:</strong>
     54 If your <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
     55 targetSdkVersion}</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
     57 as possible&mdash;while still providing your app the performance and web standards upgrades.
     58 Beware, though, that <a href="#Columns">single and narrow column layouts</a> and <a
     59 href="#Zoom">default zoom levels</a> are
     60 <strong>not supported at all</strong> on Android 4.4, and there may be other behavioral differences
     61 that have not been identified, so be sure to test your app on Android 4.4
     62 or higher even if you keep your <a
     63 href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
     64 targetSdkVersion}</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
     68 Chrome on your desktop by calling
     69 {@link android.webkit.WebView#setWebContentsDebuggingEnabled setWebContentsDebuggingEnabled()}.
     70 This new feature in {@link android.webkit.WebView} allows
     71 you to inspect and analyze your web content, scripts, and network activity while running in
     72 a {@link android.webkit.WebView}. For more information, see <a
     73 href="https://developers.google.com/chrome-developer-tools/docs/remote-debugging">Remote
     74 Debugging 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
     82 to be aware of the user agent string has changed slightly and now includes the Chrome version:</p>
     83 
     84 <pre class="no-pretty-print">
     85 Mozilla/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
     90 do not want to instantiate {@link android.webkit.WebView}, you should use
     91 the static method, {@link android.webkit.WebSettings#getDefaultUserAgent
     92 getDefaultUserAgent()}. 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
    102 UI thread, it can cause unexpected results. For example, if your app uses multiple threads,
    103 you can use the {@link android.app.Activity#runOnUiThread runOnUiThread()} method
    104 to ensure your code executes on the UI thread:</p>
    105 
    106 <pre>
    107 runOnUiThread(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">
    116 never block the UI thread</a>. A situation in which some apps make this mistake is while waiting for
    117 a 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
    121 webView.loadUrl("javascript:fn()");
    122 while(result == null) {
    123   Thread.sleep(100);
    124 }
    125 </pre>
    126 
    127 <p>You can instead use a new method, {@link android.webkit.WebView#evaluateJavascript
    128 evaluateJavascript()}, 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
    137 and 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
    143 notice that your app is receiving fewer calls to these callbacks or failing
    144 to 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
    149 for 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
    156 loadData()} or {@link android.webkit.WebView#loadDataWithBaseURL
    157 loadDataWithBaseURL()} with an invalid or null base URL, then you will not receive the
    158 {@link android.webkit.WebViewClient#shouldOverrideUrlLoading shouldOverrideUrlLoading()} callback
    159 for this type of link on the page.
    160   <p class="note"><strong>Note:</strong>
    161   When you use {@link android.webkit.WebView#loadDataWithBaseURL
    162 loadDataWithBaseURL()} and the base URL is invalid or set null, all links in the content
    163 you are loading must be absolute.</p>
    164   <li>If you loaded the page by calling {@link android.webkit.WebView#loadUrl
    165 loadUrl()} or provided a valid base URL with {@link android.webkit.WebView#loadDataWithBaseURL
    166 loadDataWithBaseURL()}, then you will receive the
    167 {@link android.webkit.WebViewClient#shouldOverrideUrlLoading shouldOverrideUrlLoading()} callback
    168 for this type of link on the page, but the URL you receive will be absolute, relative
    169 to 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
    175 as 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
    181 like this:</p>
    182 
    183 <pre>
    184 // The URL scheme should be non-hierarchical (no trailing slashes)
    185 private static final String APP_SCHEME = "example-app:";
    186 
    187 &#64;Override
    188 public 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
    200 consisting 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>
    204 webView.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>
    210 and it's important to include the trailing slash at the end, otherwise, any requests from the
    211 loaded 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
    222 property is no longer supported and you should migrate to using standard solutions with
    223 images and CSS as discussed in <a
    224 href="http://developers.google.com/chrome/mobile/docs/webview/pixelperfect">Pixel-Perfect UI in
    225 the 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"
    231 it would be set to "device-width", and if you set the viewport height to a value less than or
    232 equal to the {@link android.webkit.WebView} height, it would be set to "device-height". However,
    233 when running in the new {@link android.webkit.WebView}, the width or height value is adhered and
    234 the {@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}
    240 would merge the properties from all the tags.
    241 In the new {@link android.webkit.WebView}, only the last viewport is
    242 used 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
    249 the initial zoom level on a page have are no longer supported and you should instead define
    250 the 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
    253 at 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
    262 is given a larger viewport. For example:</p>
    263 
    264 <pre>
    265 WebSettings settings = webView.getSettings();
    266 settings.setUseWideViewPort(true);
    267 settings.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}
    280 if you also specify the {@code background} style. For example, the size here will be reset
    281 to 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
    303 href="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"
    306 class="external-link"><code>window.outerHeight</code></a> returned a value in actual screen pixels.
    307 In 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
    310 sizing elements or other calculations. However, if you've disabled zooming and the initial-scale
    311 is set to 1.0, you can use <code>window.devicePixelRatio</code>
    312 to get the scale, then multiply the CSS pixel value by that. Instead,
    313 you can also <a href="{@docRoot}guide/webapps/webview.html#BindingJavaScript">create a
    314 JavaScript 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"
    317 href="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
    324 android.webkit.WebSettings.LayoutAlgorithm} is not be supported in the new {@link
    325 android.webkit.WebView}.</p>
    326 
    327 <p class="caution"><strong>Caution:</strong> These APIs are not supported on Android 4.4 and higher
    328 at 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
    353 was previously deprecated&mdash;is also not supported in the new {@link
    354 android.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},
    362 be sure you are also handling the <a
    363 href="https://developer.mozilla.org/en-US/docs/Web/Reference/Events/touchcancel"
    364 class="external-link"><code>touchcancel</code></a>
    365 event. There are a few scenarios where <code>touchcancel</code> will be called, which can
    366 cause 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