苏州网站开发公司兴田德润简介/推广用哪个平台效果好

苏州网站开发公司兴田德润简介,推广用哪个平台效果好,网站建设哪些好,建筑企业办公系统公司用命令模式设计一个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.mzph.cn/diannao/70366.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

昇腾DeepSeek模型部署优秀实践及FAQ

2024年12月26日&#xff0c;DeepSeek-V3横空出世&#xff0c;以其卓越性能备受瞩目。该模型发布即支持昇腾&#xff0c;用户可在昇腾硬件和MindIE推理引擎上实现高效推理&#xff0c;但在实际操作中&#xff0c;部署流程与常见问题困扰着不少开发者。本文将为你详细阐述昇腾 De…

vscode复制到下一行

linux中默认快捷键是ctrl shift alt down/up 但是在vscode中无法使用&#xff0c;应该是被其他的东西绑定了&#xff0c;经测试&#xff0c;可以使用windows下的快捷键shift alt down/up { “key”: “shiftaltdown”, “command”: “editor.action.copyLinesDownAction”…

网络爬虫学习:借助DeepSeek完善爬虫软件,实现模拟鼠标右键点击,将链接另存为本地文件

一、前言 最近几个月里&#xff0c;我一直在学习网络爬虫方面的知识&#xff0c;每有收获都会将所得整理成文发布&#xff0c;不知不觉已经发了7篇日志了&#xff1a; 网络爬虫学习&#xff1a;从百度搜索结果抓取标题、链接、内容&#xff0c;并保存到xlsx文件中 网络爬虫学…

Arduino 第十六章:pir红外人体传感器练习

Arduino 第十六章&#xff1a;PIR 传感器练习 一、引言 在 Arduino 的众多有趣项目中&#xff0c;传感器的应用是非常重要的一部分。今天我们要学习的主角是 PIR&#xff08;被动红外&#xff09;传感器。PIR 传感器能够检测人体发出的红外线&#xff0c;常用于安防系统、自动…

CV -- YOLOv8 图像分割(GPU环境)

目录 参考视频&#xff1a; 标注 JSON转为TXT 训练 验证 参考视频&#xff1a; 使用 Yolov8 自定义数据集进行图像分割_哔哩哔哩_bilibili 标注 数据集&#xff1a; 我使用的是一些苹果数据集&#xff0c;可以在我的csdn资源中下载&#xff1a; https://download.csdn.net/do…

基于微信小程序的电影院订票选座系统的设计与实现,SSM+Vue+毕业论文+开题报告+任务书+指导搭建视频

本系统包含用户、管理员两个角色。 用户角色&#xff1a;注册登录、查看首页电影信息推荐、查看电影详情并进行收藏预定、查看电影资讯、在线客服、管理个人订单等。 管理员角色&#xff1a;登录后台、管理电影类型、管理放映厅信息、管理电影信息、管理用户信息、管理订单等。…

【Linux网络编程】应用层协议HTTP(请求方法,状态码,重定向,cookie,session)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux网络编程 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 ​ Linux网络编程笔记&#xff1a; https://blog.cs…

Vue3 打造 Windows 桌面个性高效组件工具

软件介绍 Widgets 这款基于 Vue3 构建的开源 Windows 桌面小部件工具超实用。 其多样化组件库涵盖超 20 种&#xff0c;从倒计时、打工进度等实用工具&#xff0c;到抖音热榜等实时资讯组件应有尽有&#xff0c;各组件独立运行&#xff0c;满足多场景需求。 高度自定义布局支持…

DeepSeek预测25考研分数线

25考研分数马上要出了。 目前&#xff0c;多所大学已经陆续给出了分数查分时间&#xff0c;综合往年情况来看&#xff0c;每年的查分时间一般集中在2月底。 等待出成绩的日子&#xff0c;学子们的心情是万分焦急&#xff0c;小编用最近爆火的“活人感”十足的DeepSeek帮大家预…

Android 动态加入Activity 时 manifest 注册报错解决。使用manifestPlaceholders 占位

需求如下&#xff1a; 项目 测试demo 有多个渠道&#xff0c;部分渠道包含支付功能&#xff0c;在主测试代码外&#xff0c;需要一个单独 Activity 调用测试代码。 MainActivityPayActivity渠道A包含不包含渠道B包含包含 因为支付功能需要引入对应的 moudule&#xff0c;因此…

FRRouting配置与OSPF介绍,配置,命令,bfd算法:

文章目录 1、frrouting的配置&#xff1a;2、ospf2.1、检测和维护邻居关系2.2、ospfDR和BDR2.3、odpf邻居表2.4、ospf常用命令2.5、bfd配置 1、frrouting的配置&#xff1a; sudo service zebra start sudo service ospfd start telnet localhost 2604 en configure termina…

Perplexity 开源DeepSeek-R1 模型新版本 R1-1776

引言 在人工智能领域&#xff0c;模型的更新迭代如同科技界的时尚潮流&#xff0c;不断推陈出新。今天&#xff0c;我们要介绍的是Perplexity AI开源的新版推理模型——DeepSeek-R1 1776。这个版本不仅继承了前代的强大性能&#xff0c;还在公正性和准确性上进行了显著提升。那…

关系中出现这10个信号,离分手就不远了(爱情友情都适用)

亲密关系的隐形裂痕 在一个阳光明媚却略显萧瑟的午后&#xff0c;咖啡杯里的咖啡已经凉透。小李盯着手机屏幕&#xff0c;那些曾经热烈的对话记录现在看起来如此陌生&#xff0c;仿佛隔着一层薄薄的雾。她终于意识到&#xff0c;这段关系已经悄然走向尽头。 亲密关系是一场精心…

马斯克Grok3使用入口和订阅教程(白嫖150刀API额度)

文章目录 马斯克Grok3使用入口和订阅教程(白嫖150刀API额度)前言介绍如何订阅X上Premium服务国内订阅X的Premium教程 白嫖教程小结 马斯克Grok3使用入口和订阅教程(白嫖150刀API额度) 前言介绍 重点&#xff1a;Grok 3仅对X上的Premium用户开放。你需要订阅X的Premium服务才能…

DeepBI助力跨境电商打破流量垄断:AI驱动的亚马逊广告投放新打法

#亚马逊广告优化# 亲爱的亚马逊跨境电商卖家们&#xff0c;是否曾因亚马逊的广告打法不清晰&#xff0c;或是纠结于亚马逊广告费用过高&#xff0c;或是为亚马逊电商广告怎么投放合适的问题而苦恼&#xff1f;在竞争激烈的亚马逊市场中&#xff0c;广告投放效果平平&#xff0…

单元测试junit5

一、idea 安装自动化生成插件jcode5 安装可能不成功&#xff0c;尝试多次安装&#xff1b; 安装成功后&#xff0c;重启idea&#xff0c;再次确认安装是否成功&#xff1b; 二、在需要生成单元测试代码的模块的pom中引入依赖 ......<parent><groupId>org.springf…

windows系统本地部署DeepSeek-R1全流程指南:Ollama+Docker+OpenWebUI

本文将手把手教您使用OllamaDockerOpenWebUI三件套在本地部署DeepSeek-R1大语言模型&#xff0c;实现私有化AI服务搭建。 一、环境准备 1.1 硬件要求 CPU&#xff1a;推荐Intel i7及以上&#xff08;需支持AVX2指令集&#xff09; 内存&#xff1a;最低16GB&#xff0c;推荐…

Windows Server 任务计划

背景&#xff1a;QMT服务只能在windows服务器上面运行 直接在控制面板里面搜索"任务计划"点进去 需要注意的是&#xff1a; 1.创建任务计划选择”不管用户是否登录都运行“&#xff0c;是否需要最高权限执行看你的需求 2.操作程序或脚本&#xff0c;选择bat脚本就…

人工智能学习环境配置

文章目录 Python、CUDA、cuDNN、PyTorch 和 Anaconda 的介绍PythonCUDAcuDNNPyTorchAnaconda联系 安装n卡驱动更新安装 AnacondaAnaconda配置镜像源CUDA安装Cudnn安装Anaconda初始化创建虚拟环境安装 PyTorch验证安装 可能的错误SSL错误 Python、CUDA、cuDNN、PyTorch 和 Anaco…

【C语言】C语言 食堂自动化管理系统(源码+数据文件)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 【C语言】C语言 食堂自动化管理系统&#xff08;源…