Android

关注公众号 jb51net

关闭
首页 > 软件编程 > Android > Android App和WebView交互

Android原生App和WebView的交互方式详解

作者:Vic_wkx

在移动开发中,我们有时候会遇到这样的需求有一部分功能需要网页实现,另一部分功能需要原生实现,这时候 Hybrid App(原生 + WebView 混合应用) 就派上用场了,本文带你全面了解 Android 原生 App 和 WebView 的交互方式,并附上实战示例,需要的朋友可以参考下

一、前言

在移动开发中,我们有时候会遇到这样的需求:

这时候 Hybrid App(原生 + WebView 混合应用) 就派上用场了。

本文带你全面了解 Android 原生 App 和 WebView 的交互方式,并附上实战示例。

二、交互

WebView 与原生 App 的交互也就两种:

  1. 网页调用 App 原生方法(JS → Native)
  2. App 调用网页 JS 方法(Native → JS)

双向通信的典型场景:

场景方向示例
网页点击按钮调用 app 功能JS → Nativewindow.myApp.nativeMethod('a')
App 收集设备信息反馈给网页Native → JSwebView.evaluateJavascript("jsMethod('a', 'b')")
登录状态同步双向网页通知 App 用户登录了,App 也可以主动查询网页是否已登录

2.1 编写本地 html

写一个本地的 html 文件 test_login.html,内容如下:

<html>
<head><meta charset="utf-8"><title>Login Demo</title></head>
<body>
<h2>Hybrid Login Demo</h2>
<button onclick="login()">Login</button>
<button onclick="logout()">Logout</button>

<script>
    window.loginState = { isLoggedIn: false };

    window.isUserLoggedIn = function() {
      console.log("isUserLoggedIn = " + window.loginState.isLoggedIn);
      return window.loginState.isLoggedIn;
    }

    function login() {
      window.loginState.isLoggedIn = true;
      console.log("Login success!");
      if (window.myApp && window.myApp.onLoginStateChanged) {
        window.myApp.onLoginStateChanged(true);
      }
    }

    function logout() {
      window.loginState.isLoggedIn = false;
      console.log("Logout success!");
      if (window.myApp && window.myApp.onLoginStateChanged) {
        window.myApp.onLoginStateChanged(false);
      }
    }
</script>
</body>
</html>

运行效果:

可以看到,页面内容很简单,一个 title,两个按钮。一个用于登入,一个用于登出。

html 中维护了一个 loginState.isLoggedIn 属性,表示用户是否已登录。

提供了一个 isUserLoggedIn 函数,用于查询当前登录状态。

另外,还有一个 login 和一个 logout 方法,分别用于模拟登入登出,当状态改变后,通过 window.myApp.onLoginStateChanged 回调通知 app 登陆状态发生了改变。

2.2 编写 app

为了便于测试,我们将 test_login.html 文件,放在 assets 文件夹下,app 上的 WebView 直接加载本地 url 即可。

MainActivity 完整代码:

package com.example.interaction

import android.os.Bundle
import android.webkit.CookieManager
import android.webkit.WebChromeClient
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.example.interaction.ui.theme.WebViewJsInteractionDemoTheme

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            WebViewJsInteractionDemoTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    LoginWebView(modifier = Modifier.padding(innerPadding))
                }
            }
        }
    }
}

@Composable
fun LoginWebView(modifier: Modifier = Modifier) {
    var loginStatus by remember { mutableStateOf("Unknown") }
    val context = LocalContext.current
    val webViewRef = remember { mutableStateOf<WebView?>(null) }

    Column(modifier = modifier.fillMaxSize()) {
        AndroidView(
            modifier = Modifier
                .weight(1f)
                .fillMaxWidth(),
            factory = { context ->
                WebView(context).apply {
                    settings.apply {
                        javaScriptEnabled = true
                        domStorageEnabled = true
                        allowFileAccess = true
                        allowContentAccess = true
                        cacheMode = WebSettings.LOAD_DEFAULT
                    }
                    webChromeClient = WebChromeClient()

                    webViewClient = object : WebViewClient() {
                        override fun onPageFinished(view: WebView?, url: String?) {
                            super.onPageFinished(view, url)
                            // Query login status when page is loaded
                            evaluateJavascript("isUserLoggedIn()") { result ->
                                val isLoggedIn = result?.contains("true") == true
                                loginStatus = if (isLoggedIn) "Logged In" else "Logged Out"
                            }
                        }
                    }

                    // Register the JavaScript interface
                    addJavascriptInterface(object {
                        @android.webkit.JavascriptInterface
                        fun onLoginStateChanged(isLoggedIn: Boolean) {
                            (context as ComponentActivity).runOnUiThread {
                                loginStatus = if (isLoggedIn) "Logged In" else "Logged Out"
                                Toast.makeText(context, "Login status changed: $loginStatus", Toast.LENGTH_SHORT).show()
                            }
                        }
                    }, "myApp")

                    WebView.setWebContentsDebuggingEnabled(true)
                    CookieManager.getInstance().setAcceptCookie(true)

                    loadUrl("file:///android_asset/test_login.html")
                    webViewRef.value = this
                }
            }
        )

        Spacer(modifier = Modifier.height(16.dp))

        // Check login status button
        Button(
            onClick = {
                webViewRef.value?.evaluateJavascript("isUserLoggedIn()") { result ->
                    val isLoggedIn = result?.contains("true") == true
                    loginStatus = if (isLoggedIn) "Logged In" else "Logged Out"
                    Toast.makeText(context, "Login status: $loginStatus", Toast.LENGTH_SHORT).show()
                }
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 16.dp)
        ) {
            Text("Check Login Status")
        }

        Spacer(modifier = Modifier.height(8.dp))

        Text(
            text = "Current Status: $loginStatus",
            modifier = Modifier.padding(horizontal = 16.dp)
        )
    }
}

运行效果:

可以看到,在 MainActivity 中,通过 addJavascriptInterface 函数添加了 onLoginStateChanged 接口供 Web 端调用,添加接口时,第二个参数是 name,Web 端将通过 name.接口名 来调用对应的接口,例如:window.myApp.onLoginStateChanged(true);

在点击 Check Login Status 按钮后,通过 WebView 的 evaluateJavascript 函数调用网页端的 isUserLoggedIn 函数,收到 result 后,更新 loginStatus 变量。

另外,还自定义了 WebViewClient,在 onPageFinished 调用后,主动调用一次 isUserLoggedIn 函数,完成 Current Status 的初始化。

三、后话

有一些需要注意的点:

附:一些常见的问题

注:不保真

在 WebView 中,通过 webView.settings 可以获取到 WebSettings,它可以用来配置一系列网页渲染与访问能力。以下是关键属性解释:

属性作用是否常用注意事项
javaScriptEnabled = true启用网页中的 JavaScript 执行。没有这个,网页的交互和动态内容几乎全失效。✅ 必须启用 JS 后要配合 addJavascriptInterface 谨慎使用,否则存在安全隐患。
domStorageEnabled = true启用 HTML5 的 DOM Storage(localStorage / sessionStorage)。网页才能保存本地状态。✅ 常用现代 Web 必备。
databaseEnabled = true启用 Web SQL 数据库(旧标准)。⚠️ 较旧新网页一般用 IndexedDB。
allowFileAccess = true允许访问本地文件(file://)。✅ 常用某些 WebView 资源加载或本地调试需要。
allowContentAccess = true允许访问 content:// URI 内容(如系统媒体)。✅ 常用安全风险低。
allowFileAccessFromFileURLs = true允许网页 JS 从 file:// 页面访问其他本地文件。⚠️ 慎用容易被恶意网页利用本地文件。
allowUniversalAccessFromFileURLs = true允许 file:// 页面访问任意网络资源(http/https)。⚠️ 高风险建议仅限调试环境启用。
useWideViewPort = true启用自适应宽度,让网页以「网页比例」显示而非手机分辨率。✅ 常用loadWithOverviewMode 一起使用更佳。
loadWithOverviewMode = true缩放网页以适配屏幕宽度。✅ 常用常配合 responsive 页面。
setSupportZoom(true)支持缩放。✅ 常用可搭配手势操作。
builtInZoomControls = true启用内建缩放按钮。✅ 可选通常在调试或旧网页中启用。
displayZoomControls = false隐藏默认的缩放控件(仅保留手势缩放)。✅ 推荐提升视觉体验。
cacheMode = WebSettings.LOAD_DEFAULT启用缓存策略。✅ 常用可选 LOAD_NO_CACHE 禁止缓存。

WebViewClient 和 WebChromeClient 的区别:

对比项WebViewClientWebChromeClient
职责控制页面导航与加载逻辑控制网页中“浏览器行为”与 UI 事件
常用回调shouldOverrideUrlLoadingonPageStartedonPageFinishedonReceivedErroronProgressChangedonReceivedTitleonConsoleMessageonJsAlert
场景举例拦截跳转、处理自定义 URL Scheme、控制加载动画显示网页标题、监控加载进度、拦截 JS 弹窗、打印调试信息
比喻浏览器“司机”浏览器“仪表盘”
建议必须设置一个(否则无法处理跳转)可选(但调试与交互建议加)

总结一句话

WebViewClient 负责“页面去哪”,WebChromeClient 负责“页面看起来怎样”。

其他关键配置:

配置作用
setLayerType(View.LAYER_TYPE_HARDWARE, null)启用硬件加速,提升渲染性能(尤其是视频或动画)。
setOnLongClickListener { true } + isLongClickable = false禁用长按(防止复制或保存图片)。
WebView.setWebContentsDebuggingEnabled(true)允许通过 Chrome 调试网页内容(chrome://inspect)。
CookieManager.getInstance().setAcceptThirdPartyCookies(...)

以上就是Android原生App和WebView的交互方式详解的详细内容,更多关于Android App和WebView交互的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文