在 WebView 中构建 Web 应用

使用 WebView 提供 Web 应用 或网页作为客户端应用的一部分。WebView 类是一个 Android View 类的扩展,可让 您将网页作为 activity 布局的一部分显示。不包含 功能完善的网络浏览器的功能,如导航控件或 地址栏。默认情况下,所有 WebView 均会显示网页。

WebView可以帮助您在应用中提供完成以下操作所需的信息 例如最终用户协议或用户指南。在 Android 应用中 您可以创建一个 Activity,并在其中包含 WebView,然后使用它来显示在线托管的文档。

WebView应用还会向用户提供需要 以检索电子邮件等数据。在这种情况下,您可以 发现在 Android 应用中构建 WebView 来显示网站 而不是执行网络请求 解析数据并在 Android 布局中呈现这些数据。您可以改为设计 专门为采用 Android 的设备打造的网页,然后实现 在用于加载网页的 Android 应用中设置 WebView

本文档介绍了如何开始使用 WebView,以及如何在 将 JavaScript 从您的网页中传递到 Android 应用中的客户端代码,如何 处理页面导航,以及如何在使用 WebView 时管理窗口。

在早期版本的 Android 上使用 WebView

若要在您的应用设备上安全地使用较新的 WebView 功能 请添加 AndroidX Webkit 库。这是一个静态 库,您可以使用android.webkit 适用于早期平台版本。

将其添加到 build.gradle 文件中,如下所示:

Kotlin

dependencies {
    implementation("androidx.webkit:webkit:1.8.0")
}

Groovy

dependencies {
    implementation ("androidx.webkit:webkit:1.8.0")
}

探索WebView 示例

向应用添加 WebView

如需向应用添加 WebView,您可以在<WebView> 活动布局,或者将整个Activity窗口设置为WebView onCreate()

在 activity 布局中添加 WebView

如需在布局中向应用添加 WebView,请将以下代码添加到 activity 的布局 XML 文件:

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

如需在 WebView 中加载网页,请使用 loadUrl(),采用 如以下示例中所示:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.loadUrl("http://www.example.com")

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");

在 onCreate() 中添加 WebView

如需在 activity 的 onCreate() 方法中向应用添加 WebView,请改用 逻辑类似于:

Kotlin

val myWebView = WebView(activityContext)
setContentView(myWebView)

Java

WebView myWebView = new WebView(activityContext);
setContentView(myWebView);

然后加载页面:

Kotlin

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

Java

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

或者通过 HTML 字符串加载网址:

Kotlin

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
val unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING)
myWebView.loadData(encodedHtml, "text/html", "base64")

Java

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
String unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
        Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

您的应用必须能够访问互联网。要获取互联网访问权限,请 INTERNET 权限 清单文件,如以下示例所示:

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

您可以通过执行以下任一操作来自定义 WebView

  • 使用以下代码启用全屏支持 WebChromeClient。此课程 当 WebView 需要相应权限才能更改托管应用的界面时,也会调用此方法。 例如创建或关闭窗口,或者将 JavaScript 对话框发送到 用户。如要详细了解如何在上述情况下进行调试,请参阅调试网站 应用
  • 处理影响内容呈现的事件,例如表单错误 提交或导航 WebViewClient。您还可以使用 来拦截网址加载。
  • 通过修改来启用 JavaScript WebSettings
  • 使用 JavaScript 访问已注入的 Android 框架对象 转换为 WebView

在 WebView 中使用 JavaScript

如果您要在 WebView 中加载的网页使用 JavaScript,则必须 为 WebView 启用 JavaScript。启用 JavaScript 后,您可以执行以下操作: 在应用代码和 JavaScript 代码之间创建接口。

启用 JavaScript

JavaScript 在 WebView 中默认处于停用状态。您可以通过 WebSettings已附加到您的WebView。使用以下字段检索 WebSettingsgetSettings(),然后启用 JavaScript 与 setJavaScriptEnabled()

请参阅以下示例:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.settings.javaScriptEnabled = true

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

您可以通过WebSettings访问其他各种设置 实用。例如,如果您要开发一款 WebView,则您可以定义一个自定义 用户代理字符串中, setUserAgentString(), 然后在您的网页中查询自定义用户代理,以验证客户端 就是您的 Android 应用

将 JavaScript 代码绑定到 Android 代码

在开发专为 WebView 设计的 Web 应用时 可以在您的 JavaScript 代码与 客户端 Android 代码例如,您的 JavaScript 代码可以调用 您的 Android 代码以显示 Dialog, 而不是使用 JavaScript 的 alert() 函数。

要在 JavaScript 和 Android 代码之间绑定新接口,请调用 addJavascriptInterface()、 向其传递一个类实例以绑定到您的 JavaScript 和一个接口名称 即可访问该类

例如,您可以在 Android 应用中包含以下类:

Kotlin

/** Instantiate the interface and set the context.  */
class WebAppInterface(private val mContext: Context) {

    /** Show a toast from the web page.  */
    @JavascriptInterface
    fun showToast(toast: String) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show()
    }
}

Java

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();
    }
}

在此示例中,WebAppInterface 类允许网页创建 Toast 消息,使用 showToast() 方法。

您可以使用以下代码将此类绑定到在 WebView 中运行的 JavaScript。 addJavascriptInterface(),如以下示例所示:

Kotlin

val webView: WebView = findViewById(R.id.webview)
webView.addJavascriptInterface(WebAppInterface(this), "Android")

Java

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

这将创建一个名为 Android 的接口,用于在 WebView。此时,您的 Web 应用可以访问 WebAppInterface 类。例如,以下是一些 在用户点按按钮时,使用新界面创建消息框消息:

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

<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

无需从 JavaScript 初始化 Android 接口。通过 WebView 会自动将其提供给您的网页。因此,当用户 点按按钮,showAndroidToast() 函数使用 Android 界面 调用 WebAppInterface.showToast() 方法。

处理网页导航

默认情况下,当用户在 WebView 中点按网页中的链接时 启动处理网址的应用。通常情况下,默认网络浏览器会打开并 加载目标网址。不过,您可以替换此行为, WebView,以便在您的 WebView 中打开链接。然后,您可以让用户 向后和向前浏览保留的网页历史记录 由您的WebView提供。

如需打开用户点按的链接,请为您的 WebView 提供 WebViewClient 使用 setWebViewClient()。 用户点按的所有链接都会加载到您的 WebView 中。如果您想进一步掌控 用于加载所点击的链接的位置,请创建您自己的 WebViewClient 以替换 shouldOverrideUrlLoading() 方法。以下示例假定 MyWebViewClient 是一个内部类 共 Activity 个。

Kotlin

private class MyWebViewClient : WebViewClient() {

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        if (Uri.parse(url).host == "www.example.com") {
            // This is your website, so don't override. Let your WebView load
            // the page.
            return false
        }
        // Otherwise, the link isn't for a page on your site, so launch another
        // Activity that handles URLs.
        Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
            startActivity(this)
        }
        return true
    }
}

Java

private class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        if ("www.example.com".equals(request.getUrl().getHost())) {
      // This is your website, so don't override. Let your WebView load the
      // page.
      return false;
    }
    // Otherwise, the link isn't for a page on your site, so launch another
    // Activity that handles URLs.
    Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
    startActivity(intent);
    return true;
  }
}

然后,为 WebView 创建这一新 WebViewClient 的实例:

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = MyWebViewClient()

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());

现在,当用户点按链接时,系统会调用 shouldOverrideUrlLoading() 方法,用于检查网址主机是否匹配 特定域,如前面的示例中所定义。如果匹配,那么 该方法会返回 false,并且不会替换网址加载。它让 WebView 会照常加载网址。如果网址主机不匹配,那么 创建 Intent 以启动默认 Activity 用于处理网址,它会解析为用户的默认网络浏览器。

处理自定义网址

WebView 在请求资源和解析链接时应用限制 使用自定义网址架构的广告例如,如果您实现了 shouldOverrideUrlLoading()shouldInterceptRequest(), 然后,WebView 仅针对有效网址调用这些方法。

例如,WebView 可能不会调用您的 shouldOverrideUrlLoading() 方法 链接:

<a href="showProfile">Show Profile</a>

系统会处理无效网址(如上例所示的网址) 在 WebView 中会不一致,因此我们建议您改用格式正确的网址。 您可以为贵组织的某个网域使用自定义架构或 HTTPS 网址 控件。

与上例中那样,您可以在链接中使用简单的字符串, 使用自定义架构,如下所示:

<a href="example-app:showProfile">Show Profile</a>

然后,您可以在 shouldOverrideUrlLoading() 方法中处理此网址,例如 :

Kotlin

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
const val APP_SCHEME = "example-app:"

override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
    return if (url?.startsWith(APP_SCHEME) == true) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8")
        respondToData(urlData)
        true
    } else {
        false
    }
}

Java

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
private static final String APP_SCHEME = "example-app:";

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith(APP_SCHEME)) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
        respondToData(urlData);
        return true;
    }
    return false;
}

shouldOverrideUrlLoading() API 主要用于启动 intent 。实现它时,请确保针对网址返回 false WebView 句柄。不过,您并不仅限于启动 intent。您可以 将启动 intent 替换为上述代码中的任何自定义行为 示例。

当您的 WebView 替换网址加载时,它会自动累积 来记录所访问网页的历史记录您可以通过 与goBack()goForward()

例如,以下代码展示了您的 Activity 如何使用设备“返回” 按钮向后导航:

Kotlin

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    // Check whether the key event is the Back button and if there's history.
    if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
        myWebView.goBack()
        return true
    }
    // If it isn't the Back button or there isn't web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event)
}

Java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // Check whether the key event is the Back button and if there's history.
    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
        myWebView.goBack();
        return true;
    }
    // If it isn't the Back button or there's no web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event);
}

如果您的应用使用 AndroidX AppCompat 1.6.0+,则可以简化之前的 片段:

Kotlin

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack()
    }
}

Java

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack();
    }
}

canGoBack() 方法 若有用户访问的网页历史记录,则返回 true。同样 可以使用canGoForward()执行以下操作: 检查是否存在向前历史记录。如果您不执行此检查,则 在用户浏览完历史记录后,goBack()goForward() 会执行相应操作 什么都不用做。

处理设备配置更改

在运行时,当设备配置时,会发生 activity 状态变化 更改,例如当用户旋转设备或关闭输入法编辑器时 (IME)。这些更改会导致 WebView 对象的 activity 被销毁,并且 要创建的新 activity,这也会创建一个新的 WebView 对象,用于加载 已销毁对象的网址。如需修改 activity 的默认行为,您可以 更改它处理清单中 orientation 更改的方式。了解详情 有关在运行时处理配置更改的信息,请阅读处理配置 更改

管理窗口

默认情况下,系统会忽略打开新窗口的请求。无论他们身在何处 由 JavaScript 或链接中的 target 属性打开。你可以自定义 您的 WebChromeClient,以提供您自己的行为来打开多个 窗口。

为了提高应用的安全性,最好阻止弹出式窗口和新窗口 。实现此行为的最安全方法是将 "true" 传入 setSupportMultipleWindows() 但不会覆盖 onCreateWindow() 方法,后者是 setSupportMultipleWindows() 所依赖的。此逻辑可防止 链接中使用了 target="_blank" 的网页加载。