H5和安卓交互时,H5渲染滞后的坑

article/2025/11/7 9:24:30

一、H5页面和安卓如何交互的

1.为什么会有h5和安卓的交互?

在手机app中,有时候需要在app中嵌入h5网页,能增加app的跨平台性,也就是相同的h5也可以嵌在ios平台。减少跨平台的开发成本。

同时,也能增强响应速度,减少内存消耗等优点。

2.如何交互?

主要在app中镶嵌webview的方式。webview可以看做一个内置浏览器,在webview中通过链接进入页面。

h5调移动端:在app中定义一个全局方法A,h5去调用。

移动端调h5:相同的,在h5 中定义一个全局方法B,然后app中调用。

一次完整的交互:由于h5调用app中的方法A没有返回值,需要app再主动调用一次h5的方法B,去拿app的返回值。

整个流程可以简化为:H5 --A()–> app --B()–> H5

app端代码:

前提:在安卓中引入webview。下面的代码在基于webview的框架下写的

通过移动端的@JavascriptInterface 在webview中定义一个全局的方法(window.xxx())。然后h5中直接通过window.xxx()去调用移动端方法。(代码1)

	// 入参分别为service(需要被调用的服务名),data(调用服务的参数),funcIndex(回调方法的唯一标识)@JavascriptInterfacefun callMobile(service:String, data: String, funcIndex: String) {// 睡2秒,模拟被调用时的运行时间Thread.sleep(2000)println("调用")handler.post {// 调用js中定义的方法webview.loadUrl("javascript:callbackFun('$data', '$uuid')");}}

H5代码:

定一个公共方法:(代码2)

/*** 用于缓存回调方法,使用map形式,每个回调方法都有对应的key*/
const funcIndexMap = new Map();/*** 封装的用于调用安卓方法的通用性方法* @param service 需要被调用的服务名* @param data 调用服务的参数* @param func 回调方法,用于前端处理安卓的返回值*/
export const callLocal = (service: string,data: string,func: any
) => {// 添加一个loading框,用来演示页面渲染的结果Toast.loading({duration: 0,message: "加载中...",forbidClick: true,});// 添加回调函数到mapconst uuidNo: string = uuid();funcIndexMap.set(uuidNo, func);// 调用安卓if (/(Android)/i.test(navigator.userAgent)) {//判断是安卓用户的时候执行调用安卓中定义的方法window.android.callMobile("xxxx",data, uuidNo);}
};

在页面调用(我使用的时vue3+ts的框架),给页面一个按钮定义一个事件,让该事件去调用上面的公共方法,该事件的代码为:(代码3)

defaultOffice = 'qqqq'
const back = () => {defaultOffice = "start";// 调用方法callLocal("",defaultOffice + "123",(res:any) => {defaultOffice = res;},true);};// h5中定义的回调函数,用于被安卓调用
window.callbackFun = (data: string, uuidNo: string, inBack: boolean) => {const getFunc = funcIndexMap.get(uuidNo);getFunc(data);funcIndexMap.delete(uuidNo);
};

二、问题描述

1.场景

像上面那样运行的结果,将会是下面这样:

点击按钮后,页面没有变化

在这里插入图片描述

2秒后,才出现loading,而且对应的值被修改
在这里插入图片描述
并没有出现想象中的,点击按钮就出现loading,然后2秒后值被修改。

因为按照代码逻辑,我们想要的结果是:

  1. OFFICE号初始化是qqqq

  2. 执行back() 函数,进入back后,首先会修改defaultOffice为start。此时页面上的OFFICE号显示为start

  3. 然后调用calLocal方法,进入此方法后,首先会加在loading

  4. 然后调用安卓的方法window.android.callMobile,等待安卓方法执行完后(2秒后执行完),会执行回调函数,将值改为over。

简化对比实际和预计的结果就是:

  • 实际:点击 —> 2s后 —> loading —> 改为over

  • 预计:点击 --> loading --> 2s后 —> 改为over

三、问题分析

先一句话概括:就是运行安卓的这个方法(window.android.callMobile)会阻塞js的执行

通过上述场景,我们发现,页面重新渲染是在安卓中的方法执行完成后。执行安卓方法的关键代码虽然就一句window.android.callMobile("xxxx",data, uuidNo);,但是在它之前的代码被执行后并没有将页面渲染。即使修改了defaultOffice的值,页面也没有变化。只有在等到最后安卓的方法被执行后,页面才会被重新渲染。

这一现象启发了我,让我重温了js的执行原理。最终找到了真相。

最终原因是js是单线程的,它会等上一句代码解析执行完成后,才会执行下一行。具体原理可参考:https://www.jianshu.com/p/1368d375aa66。

我们这里就是因为js执行到window.android.callMobile时,在里面有2s的时间阻塞,导致整个callLocal方法不能执行完成,从而导致整个back方法不能执行完成,所以对于页面来说你这个back方法还没执行完,我的defaultOffice参数被改成什么了我也不知道,所以页面才没被重新渲染。

为什么我们平时在方法中修改变量不会有这个问题?

因为我们在平时的js方法中修改外部变量时,方法很快被执行完毕,我们感受不到,但如果我们像下面这样写,在defaultOffice被修改后,执行一个稍微耗时的for循环,就会发现页面上office的值会在点击事件后隔一段时间才被修改为start

const back = () => {defaultOffice = "start";for (let i = 1; i<10000; i++) {console.log("i:", i)}
};

四、问题解决

一句话:使用setTiimeout()

问题根本找到了,我们需要解决的就是不要让window.android.callMobile方法的执行阻塞住我们的js线程,我们需要让它异步执行。


而根据setTimeout(fun,time)的原理:setTimeout和setInterval会将指定的代码移出本轮事件循环,得到下一轮事件循环,再检查是否到了指定时间,如果到了就执行对应代码,如果不到,就继续等待。所以它们都会等到本轮事件循环的所有同步任务都执行完,才开始执行。对于setTimeout来说,我们哪怕设置他的时间为0,它也会等到下一轮执行。

所以我们需要将callLocal方法修改为:

export const callLocal = (service: string,data: string,func: any
) => {Toast.loading({duration: 0,message: "加载中...",forbidClick: true,});const uuidNo: string = uuid();funcIndexMap.set(uuidNo, func);if (/(Android)/i.test(navigator.userAgent)) {//修改的地方setTimeout(() => {window.android.callMobile("xxxx",data, uuidNo);},0)}
};

修改之后即可完美实现。

五、优化同步异步调用

异步方法定义:

经过修改后我们使用callLocal()方法就可以像使用普通的,但是在方法的定义中去添加回调函数显得比较乱,阅读性不强,我们稍稍进行一些优化,使用Promise将它的回调函数转移到then中。

/*** 异步调用安卓方法* @param service* @param data* @param inBack*/
const asyncCallLocal = (service: string,data: string,inBack: boolean
) => {return new Promise((resolve) => {console.log("进入promise");callLocal(service,data,(data: any) => {resolve(data);},inBack);});
};

页面使用:

const back = () => {defaultOffice = "start";// 使用asyncCallLocal("",defaultOffice + "123",true).then((res:any) => {defaultOffice = res})defaultOffice = "end"};

由于这是异步方法,如果像上面那样调用,defaultOffice显示的结果将会是:

start —> end —> start123

同步方法定义:

如果想要让他同步进行修改想让defaultOffice最后被改为end,则需要定义一个同步的方法,让上面的asyncCallLocal()被同步执行即可。

定义如下:

/*** 同步调用安卓* @param service* @param data* @param inBack*/
const syncCallLocal = async (service: string,data: string,inBack: boolean
) => {let result: any = null;await asyncCallLocal(service, data, inBack).then((res) => {result = res;});return result;
};

使用:

const back = async () => {defaultOffice = "start";personalData.defaultOffice = await syncCallLocal("",defaultOffice + "123",true);defaultOffice = await syncCallLocal("",defaultOffice + "aaa",true);};

执行结果:start —> start123 —> start123aaa


http://chatgpt.dhexx.cn/article/MnTIUw20.shtml

相关文章

MAUI Blazor 权限经验分享 (定位,使用相机)

入门文章 Blazor Hybrid / MAUI 简介和实战 https://www.cnblogs.com/densen2014/p/16240966.html 在 Mac 上开发 .NET MAUI https://www.cnblogs.com/densen2014/p/16057571.html 在 Windows 上开发 .NET MAUI https://docs.microsoft.com/zh-cn/dotnet/maui/get-started/i…

UniApp 实战指南

uni-app 是一个使用 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台。 打包原生 APP 申请 AppKey 登录 DCloud 管理应用 点击…

Android Studio 插件大全(转)

转自&#xff1a;http://blog.csdn.net/alpha58/article/details/62881144 现在Android的开发者基本上都使用android Studio进行开发(如果你还在使用eclipse那也行&#xff0c;毕竟你乐意怎么样都行)。使用好Android Studio插件能大量的减少我们的工作量。 1.GsonFormat 快速…

定位技术课程设计-微信小程序校园导游系统

定位技术课程设计课程设计教学目的课程设计要求课程设计题目原题目拓展内容需求分析原理分析微信小程序API定位原理WIFI指纹定位原理路径规划算法调研详细设计总述主页面介绍学校简介页面介绍导引页面概述导引地图景点列表景点详细介绍页面搜索界面导航页面概述导航逻辑驾车导航…

前端知识——盒子类型,浮动属性,定位属性,JavaScript基础语法

文章目录 CSS—盒子类型marginpadding 浮动属性CSS—溢出属性overflow的设置项 CSS—定位属性定位状态定位操作 CSS—z-indexJavaScript简介主要功能运行模式变量与注释数据类型数据类型之数值类型(Number)数据类型之字符串类型(String) CSS—盒子类型 所有的标签其实都有一个…

python转js解释器_python 代码转换 js

广告关闭 腾讯云11.11云上盛惠 &#xff0c;精选热门产品助力上云&#xff0c;云服务器首年88元起&#xff0c;买的越多返的越多&#xff0c;最高返5000元&#xff01; 推荐使用腾讯云 api 配套的7种常见的编程语言 sdk&#xff0c;已经封装了签名和请求过程&#xff0c;均已…

android 图像识别定位,Android OpenCV 图像识别

最近打算写一个android 平台opencv 的小程序,着手查找了一下资料.网络上的资料参差不齐,有一些都比较老旧,我参考了前面的方法找到了一个简单的搭建方法,分享给大家. 0,环境的搭建: java 虚拟机环境搭建,网络资料很多不再赘述. 下面说明如果搭建 android opencv 环境: 下载Open…

安卓逆向及JavaScript实战

沐阳~ 各种案例&#xff0c;瑞树啥的&#xff0c;等等&#xff0c;案例多。 安卓逆向如下&#xff1a; 主&#xff1a; 沐阳~ 课程如下&#xff1a; 1.(ndk)1.初始NDK.mp4 1.(ndk)2.NDK性能提升及数据类型.mp4 1.(ndk)3.JAVA反射结合NDK.mp4 1.(ndk)4.JVM和JNI.mp4 1.(ndk)4.…

js逆向、安卓逆向教程

JS基础 提示信息 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn 1.零基础js逆向专题 MD5通杀 长度32位置 搜索关键词&#xff1a; 16进制 0x67452301 10进制 1732584193 RSA 搜索关键词&#xff1a; setpublickey AES cryptojs.aes DES cryptojs.des.encrypt …

学习强国---Android逆向及JS逆向

沐阳~ 各种案例&#xff0c;瑞树啥的&#xff0c;等等&#xff0c;案例多。有意私我&#xff0c;优惠大大 安卓逆向如下&#xff1a; 主&#xff1a; 沐阳~ 课程如下&#xff1a; 1.(ndk)1.初始NDK.mp4 1.(ndk)2.NDK性能提升及数据类型.mp4 1.(ndk)3.JAVA反射结合NDK.mp4 1.(…

js加密参数定位

当我们抓取网页端数据时&#xff0c;经常被加密参数、加密数据所困扰&#xff0c;如何快速定位这些加解密函数&#xff0c;尤为重要。本片文章是我逆向js时一些技巧的总结&#xff0c;如有遗漏&#xff0c;欢迎补充。 所需环境&#xff1a;Chrome浏览器 1. 搜索 1.1 全局搜索…

【APP 逆向百例】Frida 初体验,root 检测与加密字符串定位

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未…

巴斯勒相机acA1300-60gm

Basler acA1300-60gm GigE 相机配有 e2v EV76C560 CMOS 感光芯片&#xff0c;每秒 60 帧图像&#xff0c;130 万像素分辨率。

如何选择合适的工业相机

如何选择合适的工业相机 目录 如何选择合适的工业相机简洁具体**多相机检测****工业相机的白平衡知识**工业相机的参数工业数字相机常见问题解决方案参考文献 简洁 工业相机有许多项参数&#xff0c;选择合适的工业相机既要考虑工业相机的参数&#xff0c;也要考虑到项目的精度…

巴斯勒BASLER GIGE相机程序调试报错后需要拔网线

巴斯勒BASLER GIGE相机程序调试报错后需要拔网线的个人解决方案 根据SDK的用户手册提示&#xff0c;在相机连接后&#xff0c;直接执行以下程序。 camera.Parameters[PLTransportLayer.HeartbeatTimeout].TrySetValue(1000,IntegerValueCorrection.Nearest); // 1000 ms time…

机器视觉_工业相机及相关配件选型

文章目录 工业相机一、 概述二、 相机参数1. 传感器芯片1.1. CCD&CMOS1.2. CCD1.3. CMOS1.4. 靶面1.5. 传感器芯片选型 2. 黑白or彩色3. 帧数⭐4. 接口类型4.1. POE供电 三、相关硬件⭐1. 镜头1.1. 焦距⭐1.2. 视野⭐1.3. 物距1.4. 景深⭐1.5. 调焦1.6. 镜头的一些理论小知…

Ubuntu环境下配置巴斯勒相机及相机测试

Ubuntu环境下配置巴斯勒相机及相机测试 Ubuntu配置巴斯勒相机及相机测试软硬件要求Ubuntu虚拟系统安装安装c编译器安装Sublime Text 3及配置C运行环境配置巴斯勒相机SDK及代码测试 Ubuntu配置巴斯勒相机及相机测试 软硬件要求 软件 我们对Markdown编辑器进行了一些功能拓展与…

Python之OpenCV 005 工业相机Basler之图像采集

工业机器视觉系统2D应用用到Basler&#xff08;德国&#xff09;&#xff0c;Baumer&#xff08;瑞士&#xff09;&#xff0c;ImageSource&#xff08;台湾&#xff09;&#xff0c;大恒和海康等等牌子工业相机。 Basler&#xff08;巴斯勒&#xff09;是比较常用的&#xff…

机器视觉 · 工业相机

文章目录 工业相机 面阵相机工业相机 线阵相机工业相机 光场相机工业相机 棱镜相机工业相机 多光谱/高光谱工业相机 偏振相机工业相机 传感器 CCD工业相机 传感器 ICCD工业相机 传感器 EMCCD工业相机 传感器 CMOS工业相机 传感器 sCMOS工业相机 传感器 红外探…

BASLER巴斯勒线扫相机使用流程

(Q有答疑)康耐视智能相机Insight-OCR读取案例 1、相机连接—线缆连接 将相机电源线、网线与相机连接,网线另一端连接电脑 无编码器触发时,只需连接网线、电源线即可 2、修改电脑IP 3、修改相机IP 4、相机连接—软件连接 连接相机有两种方式: 1、双击该相机型号进行连接;…