当前位置: 首页 > news >正文

建设银行荆门招聘网站淘宝客推广有效果吗

建设银行荆门招聘网站,淘宝客推广有效果吗,手表网站排名前十,网站交互方式用命令模式设计一个JSBridge用于JavaScript与Android交互通信 在开发APP的过程中,通常会遇到Android需要与H5页面互相传递数据的情况,而Android与H5交互的容器就是WebView。 因此要想设计一个高可用的 J S B r i d g e JSBridge JSBridge,不…

用命令模式设计一个JSBridge用于JavaScript与Android交互通信

在开发APP的过程中,通常会遇到Android需要与H5页面互相传递数据的情况,而Android与H5交互的容器就是WebView。

因此要想设计一个高可用的 J S B r i d g e JSBridge JSBridge,不妨可以参考下述示例:

一、传输协议规范

设计一套用于 A n d r o i d Android Android端与 J a v a S c r i p t JavaScript JavaScript传输数据的协议规范,如下所示:

{"code": "1000001","msg": "调用成功","content": {"model": "NOH-AL00","brand": "HUAWEI"}
}

其中

  • code 字段用来表示调用的状态码
  • msg 字段用来表示调用信息
  • content 字段用来传输数据

既然是要设计到Android与JavaScript两个交互,就必然会涉及

  • Android端传输数据给JavaScript

    • 一般是通过 w e b V i e w . e v a l u a t e J a v a s c r i p t ( j a v a S c r i p t C o d e , n u l l ) webView.evaluateJavascript(javaScriptCode, null) webView.evaluateJavascript(javaScriptCode,null)
  • JavaScript端传输数据给Android

    • J S B r i d g e . c a l l N a t i v e M e t h o d ( ) JSBridge.callNativeMethod() JSBridge.callNativeMethod()

      其中要求Android端会有个统一入口,方法名叫做callNativeMethod ,然后会暴露一个JavaScript的入口webView.addJavascriptInterface(JSBridge(this, webView), “JSBridge”)

二、Android端接口

设计一个JSInterface接口,来执行Javascript调用Android回调

interface JSInterface {fun callback(webView: WebView, params: String, successFunction: String, failFunction: String?)}

让一个抽象类BaseJavaScriptHandler来实现这个接口

abstract class BaseJavaScriptHandler : JSInterface {override fun callback(webView: WebView,params: String,successFunction: String,failFunction: String?) {}    
}

三、全局注册映射不同方法对应处理类

接着不同的方法,都通过继承这个BaseJavaScriptHandler来处理各自方法的回调。比如login方法对应的处理器LoginHandler

那么前端就只需要传一个login参数过来,就可以交给LoginHandler这个类去处理,这样Android的业务代码就可以和架构代码解耦了。

class LoginHandler : BaseJavaScriptHandler() {companion object {const val KEY_ACCOUNT = "account"const val KEY_PASSWORD = "password"}override fun callback(webView: WebView,params: String,successFunction: String,failFunction: String?) {login(webView, params, successFunction, failFunction)}private fun login(webView: WebView,params: String,successFunction: String,failFunction: String?) {}}

那么接下来如何让不同的方法都映射到不同的类名里的callback方法里去呢?

答案:通过map保存对应的方法名映射到类名的关系

然后对外暴露getJavaScriptHandler方法,来获取对应的Handler实例对象来运行callback接口

object HandlerManager {const val TAG = "HandlerManager"private val map = HashMap<String, Class<out BaseJavaScriptHandler>>()fun registerJavaScriptHandler() {register(JSBridgeConstants.METHOD_NAME_LOGIN, LoginHandler::class.java)register(JSBridgeConstants.METHOD_NAME_SHOW_TOAST, ShowToastHandler::class.java)}fun getJavaScriptHandler(methodName: String) : Class<out BaseJavaScriptHandler>? {return if (map.containsKey(methodName)) {map[methodName]} else {NoSuchMethodHandler::class.java}}private fun register(methodName: String, classObject: Class<out BaseJavaScriptHandler>) {map[methodName] = classObject}}

四、统一分发不同方法执行

由于通常前端 J a v a S c r i p t JavaScript JavaScript A n d r o i d Android Android交互会有多个不同的方法调用,因此我们需要设计一个统一全局调用的收口地方,然后不同的方法通过不同的参数来区分即可。

Android端加上一个@JavascriptInterface注解,用于收敛一个与js交互的入口。

这样设计的好处是:

  • 可以统一埋点统计Javascript调用Android代码的次数
  • 收敛一个入口,找代码方便,代码简洁解耦清晰
class JSBridge(private val context: Context, private val webView: WebView) {/*** @param method 前端调用Native端的方法名* @param params 前端透传来的参数* @param successFunction 执行成功后回调给前端的方法名* @param failFunction 执行失败后回调给前端的方法名*/@JavascriptInterfacefun callNativeMethod(method: String, params: String, successFunction: String, failFunction: String) {}
} 

然后里面的实现可以通过用method方法名来解耦开来业务代码,不同的method方法对应用不同methodHandler类去解决单个方法需要执行的逻辑,这样就解耦开来了。

这样一来callNativeMethod方法的实现就好说了,如下所示:

		/*** @param method 前端调用Native端的方法名* @param params 前端透传来的参数* @param successFunction 执行成功后回调给前端的方法名* @param failFunction 执行失败后回调给前端的方法名*/@JavascriptInterfacefun callNativeMethod(method: String, params: String, successFunction: String, failFunction: String) {val javaScriptHandler = HandlerManager.getJavaScriptHandler(method)// 如果找到对应的 handler,则执行处理javaScriptHandler?.let { handler ->// 生成对应handler的实例对象                    val handlerInstance = handler.newInstance()// 触发对应handler的回调                    handlerInstance.callback(webView, params, successFunction, failFunction)} ?: run {// 如果没有找到对应的 handler,可以打印日志或显示提示Toast.makeText(context, "未找到对应的处理方法: $method", Toast.LENGTH_SHORT).show()}}

只需要在实例化全局WebView的时候,去暴露Javascript接口实例对象即可,如下所示

// 全局注册
HandlerManager.registerJavaScriptHandler()val webView: WebView = findViewById(R.id.web_container)
webView.settings.javaScriptEnabled = true
webView.webViewClient = WebViewClient()
webView.webChromeClient = WebChromeClient()// Add JSBridge interface
webView.addJavascriptInterface(JSBridge(this, webView), "JSBridge")
webView.loadUrl("file:///android_asset/index.html"))

五、前端调用

这样前端调用Android端的方法就很简单了,通过 J S B r i d g e . c a l l N a t i v e M e t h o d ( ) JSBridge.callNativeMethod() JSBridge.callNativeMethod()然后在里面传不同的方法名参数过来即可。

function login() {// Call the Android login methodJSBridge.callNativeMethod('login', JSON.stringify({account: username, password: password}), 			'onLoginSuccess', 'onLoginFail');}

六、所有代码

下面放出所有代码

HandlerManager.kt

import kotlin.collections.HashMapobject HandlerManager {const val TAG = "HandlerManager"private val map = HashMap<String, Class<out BaseJavaScriptHandler>>()fun registerJavaScriptHandler() {register(JSBridgeConstants.METHOD_NAME_LOGIN, LoginHandler::class.java)register(JSBridgeConstants.METHOD_NAME_SHOW_TOAST, ShowToastHandler::class.java)}fun getJavaScriptHandler(methodName: String) : Class<out BaseJavaScriptHandler>? {return if (map.containsKey(methodName)) {map[methodName]} else {NoSuchMethodHandler::class.java}}private fun register(methodName: String, classObject: Class<out BaseJavaScriptHandler>) {map[methodName] = classObject}}

JSInterface.kt

import android.webkit.WebViewinterface JSInterface {fun callback(webView: WebView, params: String, successFunction: String, failFunction: String?)}

BaseJavaScriptHandler.kt

import android.os.Build
import android.util.Log
import android.webkit.WebView
import org.json.JSONObjectabstract class BaseJavaScriptHandler : JSInterface {companion object {const val TAG = "BaseJavaScriptHandler"}override fun callback(webView: WebView,params: String,successFunction: String,failFunction: String?) {}fun callbackToJavaScript(webView: WebView, callbackMethod: String?, callbackParams: String?) {if (callbackMethod == null) {return}var javaScriptCode = if (callbackParams != null) {"$callbackMethod($callbackParams)"} else {"$callbackMethod()"}Log.i(TAG, "===> javaScriptCode is $javaScriptCode")MainThreadUtils.runOnMainThread(runnable = Runnable {webView.evaluateJavascript(javaScriptCode, null)})}fun getCallbackParams(code: String?, msg: String?, content: String?) : String {val params = JSONObject().apply {code?.let {put(JSBridgeConstants.KEY_CODE, code)}msg?.let {put(JSBridgeConstants.KEY_MSG, msg)}if (content == null) {put(JSBridgeConstants.KEY_CONTENT, getExtraParams().toString())} else {put(JSBridgeConstants.KEY_CONTENT, content)}}return params.toString()}fun getExtraParams(): JSONObject {val jsonObject = JSONObject().apply {put(JSBridgeConstants.KEY_BRAND, Build.BRAND)put(JSBridgeConstants.KEY_MODEL, Build.MODEL)}return jsonObject}
}

LoginHandler.kt

package com.check.webviewapplicationimport android.webkit.WebView
import android.widget.Toast
import org.json.JSONObjectclass LoginHandler : BaseJavaScriptHandler() {companion object {const val KEY_ACCOUNT = "account"const val KEY_PASSWORD = "password"}override fun callback(webView: WebView,params: String,successFunction: String,failFunction: String?) {login(webView, params, successFunction, failFunction)}private fun login(webView: WebView,params: String,successFunction: String,failFunction: String?) {val paramsObject = JSONObject(params)val account: String = paramsObject.opt(KEY_ACCOUNT) as? String ?: ""val password: String = paramsObject.get(KEY_PASSWORD) as? String ?: ""val isSuccess = checkValid(account, password)if (isSuccess) {showToast(webView, "登录成功")val callbackParams = getCallbackParams(JSBridgeConstants.CODE_SUCCESS,JSBridgeConstants.MSG_SUCCESS,getExtraParams().toString())callbackToJavaScript(webView, successFunction, callbackParams)} else {showToast(webView, "登录失败")val callbackParams = getCallbackParams(JSBridgeConstants.CODE_FAILURE,JSBridgeConstants.MSG_FAILURE,getExtraParams().toString())callbackToJavaScript(webView, failFunction, callbackParams)}}private fun checkValid(account: String, password: String) : Boolean {// 模拟账号检验流程,假设只有账号是123,密码是456的才可以检验通过return "123" == account && "456" == password}private fun showToast(webView: WebView, msg: String) {webView.context?.let {Toast.makeText(webView.context, msg, Toast.LENGTH_SHORT).show()}}}

ShowToastHandler.kt

import android.webkit.WebView
import android.widget.Toastclass ShowToastHandler : BaseJavaScriptHandler() {override fun callback(webView: WebView,params: String,successFunction: String,failFunction: String?) {webView.context?.let {Toast.makeText(webView.context, JSBridgeConstants.METHOD_NAME_SHOW_TOAST, Toast.LENGTH_SHORT).show()}val callbackParams =getCallbackParams(JSBridgeConstants.CODE_SUCCESS, JSBridgeConstants.MSG_SUCCESS, null)callbackToJavaScript(webView, successFunction, callbackParams)}}

JSBridgeConstants.kt

class JSBridgeConstants {companion object {const val METHOD_NAME_LOGIN = "login"const val METHOD_NAME_SHOW_TOAST = "showToast"const val MSG_SUCCESS =  "此方法执行成功"const val MSG_FAILURE =  "此方法执行失败"const val CODE_SUCCESS = "1"const val CODE_FAILURE = "0"const val KEY_CODE = "code"const val KEY_MSG = "msg"const val KEY_CONTENT = "content"const val VALUE_SUCCESS = "1"const val VALUE_FAILURE = "0"const val KEY_MODEL = "model"const val KEY_BRAND = "brand"}}

JSBridge.kt

import android.content.Context
import android.webkit.JavascriptInterface
import android.webkit.WebView
import android.widget.Toastclass JSBridge(private val context: Context, private val webView: WebView) {/*** @param method 前端调用Native端的方法名* @param params 前端透传来的参数* @param successFunction 执行成功后回调给前端的方法名* @param failFunction 执行失败后回调给前端的方法名*/@JavascriptInterfacefun callNativeMethod(method: String, params: String, successFunction: String, failFunction: String) {val javaScriptHandler = HandlerManager.getJavaScriptHandler(method)// 如果找到对应的 handler,则执行处理javaScriptHandler?.let { handler ->val handlerInstance = handler.newInstance()handlerInstance.callback(webView, params, successFunction, failFunction)} ?: run {// 如果没有找到对应的 handler,可以打印日志或显示提示Toast.makeText(context, "未找到对应的处理方法: $method", Toast.LENGTH_SHORT).show()}}
} 

BaseWebView.kt

import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.webkit.WebChromeClient
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Toastclass BaseWebView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : WebView(context, attrs, defStyleAttr) {init {setupWebView()}// 提供一份默认的webViewClient,同时提供自由注入业务的webViewClientprivate var webViewClient: WebViewClient = object : WebViewClient() {override fun onPageStarted(view: WebView?, url: String?, favicon: android.graphics.Bitmap?) {super.onPageStarted(view, url, favicon)// Handle page startToast.makeText(context, "Page started: $url", Toast.LENGTH_SHORT).show()}override fun onPageFinished(view: WebView?, url: String?) {super.onPageFinished(view, url)// Handle page finishToast.makeText(context, "Page finished: $url", Toast.LENGTH_SHORT).show()}override fun onReceivedError(view: WebView?,request: WebResourceRequest?,error: WebResourceError?) {super.onReceivedError(view, request, error)// Handle errorToast.makeText(context, "Error: ${error?.description}", Toast.LENGTH_SHORT).show()}}@SuppressLint("SetJavaScriptEnabled")private fun setupWebView() {// Enable JavaScriptsettings.javaScriptEnabled = true// Enable DOM storagesettings.domStorageEnabled = true// Set a WebViewClient to handle page navigationwebViewClient = getWebViewClient()// Set a WebChromeClient to handle JavaScript dialogs, favicons, titles, and the progresswebChromeClient = WebChromeClient()// Enable zoom controlssettings.setSupportZoom(true)settings.builtInZoomControls = truesettings.displayZoomControls = false// Enable cachingsettings.cacheMode = WebSettings.LOAD_DEFAULT}// Load a URLoverride fun loadUrl(url: String) {super.loadUrl(url)}// Load a URL with additional headersoverride fun loadUrl(url: String, additionalHttpHeaders: Map<String, String>) {super.loadUrl(url, additionalHttpHeaders)}// Lifecycle methodsoverride fun onResume() {}override fun onPause() {}fun onDestroy() {// Clean up WebViewclearHistory()freeMemory()destroy()}override fun setWebViewClient(client: WebViewClient) {this.webViewClient = client}override fun getWebViewClient() : WebViewClient {return webViewClient}
}

MainThreadUtils.kt

import android.os.Handler
import android.os.Looperobject MainThreadUtils {private val mainHandler = Handler(Looper.getMainLooper())/*** 判断当前是否在主线程*/fun isMainThread(): Boolean {return Looper.getMainLooper().thread === Thread.currentThread()}/*** 在主线程执行代码块* @param runnable 需要执行的代码块*/fun runOnMainThread(runnable: Runnable) {if (isMainThread()) {runnable.run()} else {mainHandler.post(runnable)}}/*** 在主线程执行代码块(使用 lambda 表达式)* @param block 需要执行的代码块*/fun runOnMainThread(block: () -> Unit) {if (isMainThread()) {block.invoke()} else {mainHandler.post { block.invoke() }}}/*** 延迟在主线程执行代码块* @param delayMillis 延迟时间(毫秒)* @param block 需要执行的代码块*/fun runOnMainThreadDelayed(delayMillis: Long, block: () -> Unit) {mainHandler.postDelayed({ block.invoke() }, delayMillis)}
}

MainActivity.kt

import android.annotation.SuppressLint
import android.os.Bundle
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivityclass MainActivity : AppCompatActivity() {@SuppressLint("SetJavaScriptEnabled")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 全局注册HandlerManager.registerJavaScriptHandler()val webView: WebView = findViewById(R.id.web_container)webView.settings.javaScriptEnabled = truewebView.webViewClient = WebViewClient()webView.webChromeClient = WebChromeClient()// Add JSBridge interfacewebView.addJavascriptInterface(JSBridge(this, webView), "JSBridge")// Load the local HTML filewebView.loadUrl("file:///android_asset/login.html")}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><WebViewandroid:id="@+id/web_container"android:layout_width="match_parent"android:layout_height="600dp"android:text="Hello World!"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"/></androidx.constraintlayout.widget.ConstraintLayout>

index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Login</title><style>body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;display: flex;justify-content: center;align-items: center;height: 100vh;margin: 0;background-color: #e9ecef;}.login-container {background-color: #fff;padding: 30px;border-radius: 10px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);width: 320px;text-align: center;}.login-container input,.login-container button {display: block;width: 100%;margin-bottom: 15px;padding: 12px;border-radius: 5px;font-size: 16px;box-sizing: border-box;}.login-container input {border: 1px solid #ddd;}.login-container button {background-color: #007BFF;color: white;border: none;cursor: pointer;transition: background-color 0.3s;}.login-container button:hover {background-color: #0056b3;}.message {margin-top: 15px;font-size: 14px;color: green;}.error {color: red;}</style>
</head>
<body><div class="login-container"><input type="text" id="username" placeholder="Username"><input type="password" id="password" placeholder="Password"><button onclick="login()">Login</button><button onclick="showToast()">ShowToast</button><div id="message" class="message"></div></div><script>function login() {var username = document.getElementById('username').value;var password = document.getElementById('password').value;// Call the Android login methodJSBridge.callNativeMethod('login', JSON.stringify({account: username, password: password}), 'onLoginSuccess', 'onLoginFail');}function showToast() {JSBridge.callNativeMethod('showToast', '', '', '');}function onLoginSuccess(response) {console.log("Raw response:", response);var messageDiv = document.getElementById('message');try {// 先将 response 转换为 JSON 字符串const jsonString = JSON.stringify(response);console.log("JSON string:", jsonString);// 然后解析为对象const params = JSON.parse(jsonString);console.log("Parsed params:", params);if (params.content) {const content = JSON.parse(params.content);console.log("Parsed content:", content);messageDiv.textContent = `Login successful! Brand: ${content.brand}, Model: ${content.model}`;} else {messageDiv.textContent = "Login successful! " + params.msg;}} catch (e) {console.error("Error parsing response:", e);messageDiv.textContent = "Login failed: " + e.message;}messageDiv.classList.remove('error');}function onLoginFail(response) {var messageDiv = document.getElementById('message');messageDiv.textContent = "Login failed!" + response;messageDiv.classList.add('error');}</script>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Login</title><style>body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;display: flex;justify-content: center;align-items: flex-end;height: 100vh;margin: 0;background-color: #e9ecef;}.login-container {background-color: #fff;padding: 30px;border-radius: 10px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);width: 320px;text-align: center;margin-bottom: 20px;}.login-container input,.login-container button {display: block;width: 100%;margin-bottom: 15px;padding: 12px;border-radius: 5px;font-size: 16px;box-sizing: border-box;}.login-container input {border: 1px solid #ddd;}.login-container button {background-color: #007BFF;color: white;border: none;cursor: pointer;transition: background-color 0.3s;}.login-container button:hover {background-color: #0056b3;}.message {margin-top: 15px;font-size: 14px;color: green;}.error {color: red;}</style>
</head>
<body><div class="login-container"><input type="text" id="username" placeholder="Username"><input type="password" id="password" placeholder="Password"><button onclick="login()">Login</button><button onclick="showToast()">ShowToast</button><div id="message" class="message"></div></div><script>function login() {var username = document.getElementById('username').value;var password = document.getElementById('password').value;// Call the Android login methodJSBridge.callNativeMethod('login', JSON.stringify({account: username, password: password}), 'onLoginSuccess', 'onLoginFail');}function showToast() {JSBridge.callNativeMethod('showToast', '', '', '');}function onLoginSuccess(response) {console.log("Raw response:", response);var messageDiv = document.getElementById('message');try {// 先将 response 转换为 JSON 字符串const jsonString = JSON.stringify(response);console.log("JSON string:", jsonString);// 然后解析为对象const params = JSON.parse(jsonString);console.log("Parsed params:", params);if (params.content) {const content = JSON.parse(params.content);console.log("Parsed content:", content);messageDiv.textContent = `Login successful! Brand: ${content.brand}, Model: ${content.model}`;} else {messageDiv.textContent = "Login successful! " + params.msg;}} catch (e) {console.error("Error parsing response:", e);messageDiv.textContent = "Login failed: " + e.message;}messageDiv.classList.remove('error');}function onLoginFail(response) {var messageDiv = document.getElementById('message');messageDiv.textContent = "Login failed!" + response;messageDiv.classList.add('error');}</script>
</body>
</html>

最后运行截图:

image-20250216224947804

用chrome://inspect/#devices还可以查看对应的JavaScript控制台输出的信息

image-20250216225112588

代码目录结构

image-20250216224328451

http://www.cadmedia.cn/news/10776.html

相关文章:

  • 保定专业网站建设开发公司百度关键词搜索排名
  • 新手如何做英文网站赚美元网站seo优化方案项目策划书
  • 青岛品牌网站建设万网官网入口
  • 线上外包平台前端优化网站
  • 阜宁网站制作哪家好江苏百度推广代理商
  • 网站建设中模seo应该怎么做
  • 各类手机网站建设市场宣传推广方案
  • 自学网站建设教程网络营销的含义的理解
  • 做哪个网站招出国打工的多一键生成app制作器
  • 武汉网站建设组织网络营销的策略包括
  • 用vs2013做网站产品的网络推广要点
  • 博远手机销售管理系统网站推广优化的方法
  • 上海百姓网免费发布信息网搜索引擎网站排名优化方案
  • 青海海东平安县建设局网站关于普通话的手抄报
  • 河北省保定市唐县城乡建设网站百度提交入口地址在哪
  • 宁波网站建设策划公司排名百度怎么优化网站排名
  • 快站优惠券去哪里找百度app下载并安装
  • 郴州网站建设有哪些哪家网络公司比较好
  • 中小企业建设网站策略网络整合营销公司
  • 信贷 网站模板 下载seo优化技术教程
  • 做免费网站网站优化分析
  • 上海单位网站建设吴中seo页面优化推广
  • 网站设计东莞扬州seo优化
  • 网站建设制作设计营销公司南宁成人企业管理培训课程
  • 广东专业网站优化制作公司seochan是什么意思
  • 汉阳网站推广今天重大新闻国内最新消息
  • 上海网易seo推广有哪些
  • 商城网站开发哪家好hyein seo
  • 苏州园区网站设计公司互动营销的案例及分析
  • 北京网站建设手机app电子商务深圳做网站的公司有哪些