最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2025年11月12日 08:53:56   作者:Vic_wkx  
在移動(dòng)開(kāi)發(fā)中,我們有時(shí)候會(huì)遇到這樣的需求有一部分功能需要網(wǎng)頁(yè)實(shí)現(xiàn),另一部分功能需要原生實(shí)現(xiàn),這時(shí)候?Hybrid?App(原生?+?WebView?混合應(yīng)用)?就派上用場(chǎng)了,本文帶你全面了解?Android?原生?App?和?WebView?的交互方式,并附上實(shí)戰(zhàn)示例,需要的朋友可以參考下

一、前言

在移動(dòng)開(kāi)發(fā)中,我們有時(shí)候會(huì)遇到這樣的需求:

  • 有一部分功能需要網(wǎng)頁(yè)實(shí)現(xiàn)(比如登錄頁(yè)、主頁(yè),已經(jīng)有網(wǎng)頁(yè)端了,不希望在 app 中再寫(xiě)一遍)
  • 另一部分功能需要原生實(shí)現(xiàn)(比如硬件訪問(wèn)、獲取系統(tǒng)權(quán)限、或者一些注重性能的邏輯)

這時(shí)候 Hybrid App(原生 + WebView 混合應(yīng)用) 就派上用場(chǎng)了。

本文帶你全面了解 Android 原生 App 和 WebView 的交互方式,并附上實(shí)戰(zhàn)示例。

二、交互

WebView 與原生 App 的交互也就兩種:

  1. 網(wǎng)頁(yè)調(diào)用 App 原生方法(JS → Native)
  2. App 調(diào)用網(wǎng)頁(yè) JS 方法(Native → JS)

雙向通信的典型場(chǎng)景:

場(chǎng)景方向示例
網(wǎng)頁(yè)點(diǎn)擊按鈕調(diào)用 app 功能JS → Nativewindow.myApp.nativeMethod('a')
App 收集設(shè)備信息反饋給網(wǎng)頁(yè)Native → JSwebView.evaluateJavascript("jsMethod('a', 'b')")
登錄狀態(tài)同步雙向網(wǎng)頁(yè)通知 App 用戶登錄了,App 也可以主動(dòng)查詢網(wǎng)頁(yè)是否已登錄

2.1 編寫(xiě)本地 html

寫(xiě)一個(gè)本地的 html 文件 test_login.html,內(nèi)容如下:

<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>

運(yùn)行效果:

可以看到,頁(yè)面內(nèi)容很簡(jiǎn)單,一個(gè) title,兩個(gè)按鈕。一個(gè)用于登入,一個(gè)用于登出。

html 中維護(hù)了一個(gè) loginState.isLoggedIn 屬性,表示用戶是否已登錄。

提供了一個(gè) isUserLoggedIn 函數(shù),用于查詢當(dāng)前登錄狀態(tài)。

另外,還有一個(gè) login 和一個(gè) logout 方法,分別用于模擬登入登出,當(dāng)狀態(tài)改變后,通過(guò) window.myApp.onLoginStateChanged 回調(diào)通知 app 登陸狀態(tài)發(fā)生了改變。

2.2 編寫(xiě) app

為了便于測(cè)試,我們將 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)
        )
    }
}

運(yùn)行效果:

可以看到,在 MainActivity 中,通過(guò) addJavascriptInterface 函數(shù)添加了 onLoginStateChanged 接口供 Web 端調(diào)用,添加接口時(shí),第二個(gè)參數(shù)是 name,Web 端將通過(guò) name.接口名 來(lái)調(diào)用對(duì)應(yīng)的接口,例如:window.myApp.onLoginStateChanged(true);。

在點(diǎn)擊 Check Login Status 按鈕后,通過(guò) WebView 的 evaluateJavascript 函數(shù)調(diào)用網(wǎng)頁(yè)端的 isUserLoggedIn 函數(shù),收到 result 后,更新 loginStatus 變量。

另外,還自定義了 WebViewClient,在 onPageFinished 調(diào)用后,主動(dòng)調(diào)用一次 isUserLoggedIn 函數(shù),完成 Current Status 的初始化。

三、后話

有一些需要注意的點(diǎn):

  • 調(diào)用 js 方法時(shí),結(jié)果是異步返回的,通過(guò) listener 接收結(jié)果。
  • @JavascriptInterface 的方法在 非 UI 線程 執(zhí)行,如果要更新 UI,需要使用 runOnUiThread。
  • 設(shè)置了 WebView.setWebContentsDebuggingEnabled(true) 之后,通過(guò) Chrome DevTools 可直接調(diào)試 WebView。方法是在 app 加載了網(wǎng)頁(yè)后,在 Chrome 瀏覽器訪問(wèn) chrome://inspect/#devices,在這里找到自己的設(shè)備,點(diǎn)擊 inspect。我對(duì)這種方式不是很熟悉,就不過(guò)多介紹了。

附:一些常見(jiàn)的問(wèn)題

注:不保真

在 WebView 中,通過(guò) webView.settings 可以獲取到 WebSettings,它可以用來(lái)配置一系列網(wǎng)頁(yè)渲染與訪問(wèn)能力。以下是關(guān)鍵屬性解釋:

屬性作用是否常用注意事項(xiàng)
javaScriptEnabled = true啟用網(wǎng)頁(yè)中的 JavaScript 執(zhí)行。沒(méi)有這個(gè),網(wǎng)頁(yè)的交互和動(dòng)態(tài)內(nèi)容幾乎全失效。? 必須啟用 JS 后要配合 addJavascriptInterface 謹(jǐn)慎使用,否則存在安全隱患。
domStorageEnabled = true啟用 HTML5 的 DOM Storage(localStorage / sessionStorage)。網(wǎng)頁(yè)才能保存本地狀態(tài)。? 常用現(xiàn)代 Web 必備。
databaseEnabled = true啟用 Web SQL 數(shù)據(jù)庫(kù)(舊標(biāo)準(zhǔn))。?? 較舊新網(wǎng)頁(yè)一般用 IndexedDB。
allowFileAccess = true允許訪問(wèn)本地文件(file://)。? 常用某些 WebView 資源加載或本地調(diào)試需要。
allowContentAccess = true允許訪問(wèn) content:// URI 內(nèi)容(如系統(tǒng)媒體)。? 常用安全風(fēng)險(xiǎn)低。
allowFileAccessFromFileURLs = true允許網(wǎng)頁(yè) JS 從 file:// 頁(yè)面訪問(wèn)其他本地文件。?? 慎用容易被惡意網(wǎng)頁(yè)利用本地文件。
allowUniversalAccessFromFileURLs = true允許 file:// 頁(yè)面訪問(wèn)任意網(wǎng)絡(luò)資源(http/https)。?? 高風(fēng)險(xiǎn)建議僅限調(diào)試環(huán)境啟用。
useWideViewPort = true啟用自適應(yīng)寬度,讓網(wǎng)頁(yè)以「網(wǎng)頁(yè)比例」顯示而非手機(jī)分辨率。? 常用loadWithOverviewMode 一起使用更佳。
loadWithOverviewMode = true縮放網(wǎng)頁(yè)以適配屏幕寬度。? 常用常配合 responsive 頁(yè)面。
setSupportZoom(true)支持縮放。? 常用可搭配手勢(shì)操作。
builtInZoomControls = true啟用內(nèi)建縮放按鈕。? 可選通常在調(diào)試或舊網(wǎng)頁(yè)中啟用。
displayZoomControls = false隱藏默認(rèn)的縮放控件(僅保留手勢(shì)縮放)。? 推薦提升視覺(jué)體驗(yàn)。
cacheMode = WebSettings.LOAD_DEFAULT啟用緩存策略。? 常用可選 LOAD_NO_CACHE 禁止緩存。

WebViewClient 和 WebChromeClient 的區(qū)別:

對(duì)比項(xiàng)WebViewClientWebChromeClient
職責(zé)控制頁(yè)面導(dǎo)航與加載邏輯控制網(wǎng)頁(yè)中“瀏覽器行為”與 UI 事件
常用回調(diào)shouldOverrideUrlLoading、onPageStartedonPageFinished、onReceivedErroronProgressChanged、onReceivedTitle、onConsoleMessage、onJsAlert
場(chǎng)景舉例攔截跳轉(zhuǎn)、處理自定義 URL Scheme、控制加載動(dòng)畫(huà)顯示網(wǎng)頁(yè)標(biāo)題、監(jiān)控加載進(jìn)度、攔截 JS 彈窗、打印調(diào)試信息
比喻瀏覽器“司機(jī)”瀏覽器“儀表盤(pán)”
建議必須設(shè)置一個(gè)(否則無(wú)法處理跳轉(zhuǎn))可選(但調(diào)試與交互建議加)

總結(jié)一句話

WebViewClient 負(fù)責(zé)“頁(yè)面去哪”,WebChromeClient 負(fù)責(zé)“頁(yè)面看起來(lái)怎樣”。

其他關(guān)鍵配置:

配置作用
setLayerType(View.LAYER_TYPE_HARDWARE, null)啟用硬件加速,提升渲染性能(尤其是視頻或動(dòng)畫(huà))。
setOnLongClickListener { true } + isLongClickable = false禁用長(zhǎng)按(防止復(fù)制或保存圖片)。
WebView.setWebContentsDebuggingEnabled(true)允許通過(guò) Chrome 調(diào)試網(wǎng)頁(yè)內(nèi)容(chrome://inspect)。
CookieManager.getInstance().setAcceptThirdPartyCookies(...)

以上就是Android原生App和WebView的交互方式詳解的詳細(xì)內(nèi)容,更多關(guān)于Android App和WebView交互的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

绥中县| 栾川县| 福鼎市| 南昌市| 清流县| 渭南市| 阜康市| 马尔康县| 内乡县| 贵州省| 信丰县| 商河县| 澄城县| 中卫市| 台东县| 平阳县| 新丰县| 筠连县| 美姑县| 镇巴县| 宁国市| 河间市| 和林格尔县| 龙口市| 凤城市| 灯塔市| 恭城| 什邡市| 天门市| 扎赉特旗| 乐至县| 南京市| 乐亭县| 唐河县| 白山市| 凤翔县| 商城县| 涪陵区| 沽源县| 湖北省| 凌源市|