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

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

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

一、前言

在移動開發(fā)中,我們有時候會遇到這樣的需求:

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

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

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

二、交互

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

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

雙向通信的典型場景:

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

2.1 編寫本地 html

寫一個本地的 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>

運行效果:

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

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

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

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

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 函數(shù)添加了 onLoginStateChanged 接口供 Web 端調(diào)用,添加接口時,第二個參數(shù)是 name,Web 端將通過 name.接口名 來調(diào)用對應(yīng)的接口,例如:window.myApp.onLoginStateChanged(true);

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

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

三、后話

有一些需要注意的點:

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

附:一些常見的問題

注:不保真

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

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

WebViewClient 和 WebChromeClient 的區(qū)別:

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

總結(jié)一句話

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

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

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

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

相關(guān)文章

最新評論

峡江县| 堆龙德庆县| 监利县| 柳江县| 绥中县| 福海县| 策勒县| 晋宁县| 威海市| 客服| 会泽县| 河曲县| 梁河县| 琼海市| 锦州市| 宣武区| 闽侯县| 澄城县| 滁州市| 保亭| 贵溪市| 宁强县| 即墨市| 区。| 三门县| 巴中市| 阳原县| 宁国市| 潮州市| 宣威市| 都江堰市| 朝阳市| 定日县| 河津市| 长海县| 古田县| 龙胜| 商都县| 铜鼓县| 疏附县| 台中县|