在WebView中构建网络应用

[原文链接地址](https://developer.android.com/guide/webapps/webview.html)

# 在WebView中构建网络应用

*如果你想在你的客户端应用上引入一个Web应用(仅仅是一个Web页),你就可以使用WebView。WebView是一个允许你在你的Activity布局中展示Web页面的View类的实现类。它不包括一个完全开发的浏览器的任何功能,如导航栏或地址栏。WebView所做的,仅仅是显示一个Web页面.*

一个更加常见的使用WebView的场景是当你想要在你的应用中提供一些信息,而这些信息很可能需要更新时而使用WebView。就像用户协议和用户指南等。在你的应用中,你可以创建一个包含WebView的Activity,利用它来展示你的在线文档。

另一个WebView可能很有用的场景就是 你的应用需要对用户提供一些数据,而这些数据需要你经常性的访问网络来获得,就像email一样。在这种情况中,你会发现相比发送一个网络请求,然后解析其中的数据最后将他们展示在你的布局中来说,使用一个web 页来展示用户数据时十分简单的。进而,你可以专门为Android设备设计一个web页,然后通过WebView在你的Android应用中加载这个页面来实现。

这篇文章向你展示了如何启用一个WebView,如何做一些额外的事情,就像处理界面导航,从Web页绑定JavaScript到Android应用的客户端代码中去。

## 为你的应用程序增加一个WebView

为了在你的应用中增加WebView,简单的在布局文件中引入 `<WebView>`标签就可以。例如,下面就是一个让WebView充满屏幕的布局文件。

```

<?xml version="1.0" encoding="utf-8"?>

<WebView  xmlns:android="http://schemas.android.com/apk/res/android"

    android:id="@+id/webview"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

/>

```

通过`loadUrl()`方法为WebView加载Web页。例如:

```

WebView myWebView = (WebView) findViewById(R.id.webview);

myWebView.loadUrl("http://www.example.com");

```

在它开始工作之前,你的应用必须有访问网络的权限。为了获得这一权限,需要在你的manifest文件中请求[INTERNET](https://developer.android.com/reference/android/Manifest.permission.html#INTERNET)权限。例如:

```

<manifest ... >

    <uses-permission android:name="android.permission.INTERNET" />

    ...

</manifest>

```

这就是创建一个基本的展示Web页的WebView你所需要做的全部的事情。

## 在WebView中使用JavaScript

如果你想在你的WebView中加载带有JavaScript的Web页,你必须让你的WebView支持JavaScript。一旦JavaScript被启用了,你就可以在你的应用代码和JavaScript代码中创建接口了。

### 启用JavaScript

默认情况下,WebView中JavaScript是不启用的。你可以通过依附于WebView的[WebSettings](https://developer.android.com/reference/android/webkit/WebSettings.html)类实现,使用webView 的`getSetting()`方法返回一个`webSetting`对象,调用`setJavaScriptEnabled()`来开启JavaScript。

例如:

```

WebView myWebView = (WebView) findViewById(R.id.webview);

WebSettings webSettings = myWebView.getSettings();

webSettings.setJavaScriptEnabled(true);

```

[WebSettings](https://developer.android.com/reference/android/webkit/WebSettings.html)提供了各种各样的你可能会发现很有用的其他设置。例如,如果你正在开发一个专门用WebView设计的Web应用,你通过`setUserAgentString()`方法自定义了一个一个用户代理字符串。通过在你的Web页中查询这个自定义的用户代理字符串来核实客户端的请求是否来自的了Android应用。

### 将 JavaScript 代码绑定到 Android代码

当我们开发一个以WebView实现Web的Android应用时,你可以在你的javaScript代码和客户端Android之前创建接口。例如你的JavaScript代码可以调用一段你的Android代码来启动一个 [Dialog](https://developer.android.com/reference/android/app/Dialog.html),而不是通过在JavaScript中使用`alert()`函数实现。

为了在你的Android代码和JavaScript代码间绑定一个接口,需要调用`addJavascriptInterface()`方法,传递一个类实例到你绑定的JavaScript中,JavaScript可以调用以访问该类的接口名称。

```

public class WebAppInterface {

    Context mContext;

    /** Instantiate the interface and set the context */

    WebAppInterface(Context c) {

        mContext = c;

    }

    /** Show a toast from the web page */

    @JavascriptInterface

    public void showToast(String toast) {

        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();

    }

}

```

> 警告:如果你的 [`targetSdkVersion`](https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#target)是17或者更高,你必须在你想要暴露给JavaScript的方法(方法必须是public方法)前加 `@JavascriptInterface`注解,如果你不提供这个注解,当在Android4.2或者更高的版本中运行该程序时,你的web 也将不能取得该方法。

在这个例子中,`WebAppInterface`类允许web页通过使用`showToast()`方法创建一个Toast信息。

你可以通过`addJavascriptInterface()`方法绑定这个类到运行在你的WebView中JavaScript上,并且以“Android”命名接口。

例如:

```

WebView webView = (WebView) findViewById(R.id.webview);

webView.addJavascriptInterface(new WebAppInterface(this), "Android");

```

这段代码为运行在WebView上的JavaScript创建了一个名为“Android”的接口,这时,你的Web应用可以访问到

`WebAppInterface`类。类如,当用户点击了一个按钮,这里有的HTML和JavaScript代码就会使用这个接口创建一个Toast消息。

```

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">

    function showAndroidToast(toast) {

        Androi.showToast(toast);

    }

</script>

```

这里不需要在JavaScript中初始化你的“Android”接口,WebView自动的让它在你的Web页中可获得。所以,在按钮的点击事件中,`showAndroidToast()`方法使用“Android”接口来调用`WebAppInterface.showToast()`方法。

> 注意:绑定到JavaScript上的对象将会运行在另一个线程中,而不是在构造它的线程中运行。

警告:使用`addJavascriptInterface()`方法会允许JavaScript控制你的Android应用。这是一个很有用的特点,也是一个危险的安全问题。当WebView中的HTML代码不可信时(例如,HTML的一部分或全部是由未知的人或进程提供的)攻击者可以植入HTML代码执行你的客户端代码,这些代码可以是任意代码。正因为如此,除非你WebView中所有的HTML和JavaScript代码都是自己写的,否则就不要使用`addJavascriptInterface()`方法。你也不应该让用户在你的WebView中导航到不属于你的Web站点上。(相反的是,允许用户使用默认浏览器应用来打开外部链接--在默认情况下,用户的浏览器可以打开所有链接,所以,在以下情况时要小心处理导航界面)

## 处理页面导航

当用户在你的WebView中点击了Web页中的一个链接,默认的行为是让Android 系统启动一个可以处理URLs的应用。通常情况下,默认的Web浏览器将会根据URL去加载。然而,你可以在你的WebView中重写这个行为,让链接在你的WebView中打开。你可以允许用户通过WebView中保存的浏览历史来通过导航栏进行前进或后退。

为了打开被用户点击的链接,简单的为WebView提供了[WebViewClient](https://developer.android.com/reference/android/webkit/WebViewClient.html)类,通过 `setWebViewClient()`方法获得,例如:

```

WebView myWebView = (WebView) findViewById(R.id.webview);

myWebView.setWebViewClient(new WebViewClient());

```

就这样,所有用户点击的链接都将在你的WebView中加载。

如果你想对链接点击事件有更多的控制,创建你自己的WebViewClient 重写 `shouldOverrideUrlLoading()`方法。例如:

```

private class MyWebViewClient extends WebViewClient {

    @Override

    public boolean shouldOverrideUrlLoading(WebView view, String url) {

        if (Uri.parse(url).getHost().equals("www.example.com")) {

            // This is my web site, so do not override; let my WebView load the page

            return false;

        }

        // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs

        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));

        startActivity(intent);

        return true;

    }

}

```

接下来,为你的WebView创建一个新的WebViewClient:

```

WebView myWebView = (WebView) findViewById(R.id.webview);

myWebView.setWebViewClient(new MyWebViewClient());

```

现在,每当用户点击一个链接,系统就会调用 `shouldOverrideUrlLoading()`方法,去检查是否URL的主机名称可以匹配这个特殊的域名。如果确实匹配,方法会返回false,不会重写URL,如果不匹配,就通过一个Intent,去启动默认程序来解析URLs。

### 浏览网页历史记录

当你的WebView重新加载了URL时,它会自动的保存一个浏览的历史记录。你可以通过导航栏的后退和前进在历史记录的 `goBack()` 和 `goForward()`方法中浏览。

例如,这里是你的Activity能使用的设备导航栏的后退按钮的方法。

```

@Override

public boolean onKeyDown(int keyCode, KeyEvent event) {

    // Check if the key event was the Back button and if there's history

    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {

        myWebView.goBack();

        return true;

    }

    // If it wasn't the Back key or there's no web page history, bubble up to the default

    // system behavior (probably exit the activity)

    return super.onKeyDown(keyCode, event);

}

```

`canGoBack()`方法返回true代表这里有用户可以访问的Web页的历史记录。同样的,你可以使用 `canGoForward()`方法来判断是否有可以前进的历史记录。如果你不做这个检查,一旦用户到达了历史记录的底端 `goBack()` 和 `goForward()` 方法将什么也不会做。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,639评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,093评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,079评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,329评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,343评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,047评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,645评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,565评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,095评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,201评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,338评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,014评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,701评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,194评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,320评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,685评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,345评论 2 358

推荐阅读更多精彩内容