)
示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载本示例来自 cw-omnibus 仓库The Busy Coders Guide to Android Development全书示例源码合辑的 WebKit 分类核心演示的是Android WebView 高级通信能力中的一个方向由 Java 侧主动把消息推送给网页中的 JavaScript其载体是 Android 6.0API 23引入的WebMessagepostWebMessage()API。示例以光传感器实时亮度推送到网页显示为实战场景同时保留了addJavascriptInterface拉取通道与老版本evaluateJavascript()降级路径读完本文你可以掌握同一功能在 Android M / KitKat / 更老版本下的完整实现方案以及 Java 与网页双向通信的四种典型姿势。示例背景与定位仓库中WebKit/SensorMessage/README.markdown开门见山地说明了这个示例的目的This sample Android app demonstrates usingWebMessageto have Java send messages to JavaScript in a Web page.即Java 代码通过WebMessage主动向网页中的 JavaScript 发送消息。整个应用把手机光传感器TYPE_LIGHT的实时读数推送到一个WebView内加载的 HTML 页面中展示是一个原生侧采集数据 → 推送进 Web 页面的典型桥接场景。该示例对应The Busy Coders Guide to Android Development一书的 Advanced Uses of WebViewWebView 高级用法章节。从仓库结构看WebKit/目录下围绕同一个传感器 网页桥接主题还配套了另外三个示例共同覆盖了 Java ↔ JavaScript 通信的全部方向示例通信方向核心机制SensorPushJava → JSevaluateJavascript()等直接调用 JSSensorPullJS → JavaaddJavascriptInterface回调SensorMessage本文Java → JS消息化WebMessagepostWebMessage()SensorPortJava ↔ JS私有通道WebMessagePort建立双向管道通信机制总览Java → JS 的三代方案在MainActivity.java中onSensorChanged()根据系统版本把亮度值分发到三种不同的推通道完整覆盖了 API 15 到 API 25 的兼容区间if (Build.VERSION.SDK_INT Build.VERSION_CODES.M) { wv.postWebMessage(new WebMessage(jsInterface.getLux()), Uri.EMPTY); // API 23本示例主角 } else if (Build.VERSION.SDK_INT Build.VERSION_CODES.KITKAT) { wv.evaluateJavascript(js, null); // API 19同步回调方式 } else { wv.loadUrl(javascript: js); // API 19 之前的老办法 }postWebMessage()Android M / API 23把封装在WebMessage中的字符串消息投递给 WebView 当前页面主 frame第二个参数Uri.EMPTY表示不限定目标源target origin。这是本示例重点示范的新机制消息经过系统级通道传递比拼装javascript:代码更规范、更安全。evaluateJavascript()Android 4.4 / API 19以回调方式执行一段 JavaScript 表达式同样可以调用页面内函数此处为update_lux(...)且能异步取回返回值。loadUrl(javascript: js)最古老的方案把 JS 代码当 URL 加载执行安全性最差、也不利于调试仅作为低版本兜底。注意由于postWebMessage()只在 Android 6.0 及以上可用而示例的minSdkVersion是 15所以这段版本分派逻辑是保证功能完整性的必要设计而非冗余。工程骨架从一个全屏 WebView 开始整个应用的 UI 极其精简——只有一个铺满屏幕的WebView布局定义在 app/src/main/res/layout/main.xmlWebView android:idid/webkit xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent /WebView清单文件 AndroidManifest.xml 声明了唯一的MainActivity作为 LAUNCHER 入口主题使用 Material Lightvalues-v21/styles.xml中基于android:Theme.Material.Light.DarkActionBar定制了主色#3f51b5、深色主色#1a237e与强调色#ffee58。该示例不申请任何权限——光传感器读数不需要运行时权限。模块构建配置见 app/build.gradleandroid { compileSdkVersion 25 buildToolsVersion 26.0.2 defaultConfig { minSdkVersion 15 targetSdkVersion 25 applicationId com.commonsware.android.webkit.bridge.message } }工程采用 Android Gradle Plugin 3.1.4见 build.gradleminSdkVersion 15与project.properties中的targetandroid-19共同解释了代码里三级降级链路的必要性。MainActivity 逐步解读传感器数据如何流向网页核心实现位于 app/src/main/java/com/commonsware/android/webkit/bridge/MainActivity.java类声明为Activity implements SensorEventListener把传感器监听与WebView 桥接糅合在同一个 Activity 中。1. 初始化传感器 WebView JS 桥private static final String URL file:///android_asset/index.html; private SensorManager mgr; private Sensor light; private WebView wv; private final JSInterface jsInterface new JSInterface(); SuppressLint({AddJavascriptInterface, SetJavaScriptEnabled}) Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mgr (SensorManager) getSystemService(Context.SENSOR_SERVICE); light mgr.getDefaultSensor(Sensor.TYPE_LIGHT); wv (WebView) findViewById(R.id.webkit); wv.getSettings().setJavaScriptEnabled(true); wv.addJavascriptInterface(jsInterface, LIGHT_SENSOR); wv.loadUrl(URL); }关键动作依次是获取光传感器通过系统服务SENSOR_SERVICE取默认的TYPE_LIGHT传感器开启 JavaScriptsetJavaScriptEnabled(true)是任何 JS 桥接的前置条件注册拉取通道addJavascriptInterface(jsInterface, LIGHT_SENSOR)把 Java 对象暴露给网页网页里以全局对象LIGHT_SENSOR访问加载页面从 assets 目录加载index.html。SuppressLint({AddJavascriptInterface, SetJavaScriptEnabled})是对应的两条 lint 检查豁免——这两项配置本身就带有安全提示属性详见文末安全小节。2. 传感器生命周期管理Override protected void onStart() { super.onStart(); mgr.registerListener(this, light, SensorManager.SENSOR_DELAY_UI); } Override protected void onStop() { mgr.unregisterListener(this); super.onStop(); }监听器在onStart()注册、onStop()注销采样延迟采用SENSOR_DELAY_UI适合更新 UI 的频率约 60ms 级别。这样保证页面不可见时停止采样避免无谓的功耗。3. 事件回调三级分派推送亮度Override public void onSensorChanged(SensorEvent sensorEvent) { float lux sensorEvent.values[0]; jsInterface.updateLux(lux); String js String.format(Locale.US, update_lux(%f), lux); if (Build.VERSION.SDK_INT Build.VERSION_CODES.M) { wv.postWebMessage(new WebMessage(jsInterface.getLux()), Uri.EMPTY); } else if (Build.VERSION.SDK_INT Build.VERSION_CODES.KITKAT) { wv.evaluateJavascript(js, null); } else { wv.loadUrl(javascript: js); } }值得注意的细节传感器每次回调都会做三件事把原始 lux 值暂存进JSInterface供网页随时拉取拼装一条直接调用 JS 函数的字符串update_lux(%f)按 API 级别选择投递通道——M 及以上用WebMessage内容是jsInterface.getLux()返回的 JSON 字符串KITKAT 用evaluateJavascript更老版本退化为loadUrl(javascript:...)。onAccuracyChanged()在此示例中为空实现因为亮度传感器没有精度概念仅供接口完整性使用。4. JSInterface暴露给网页的 Java 对象private static class JSInterface { float lux 0.0f; private void updateLux(float lux) { this.lux lux; } JavascriptInterface public String getLux() { return (String.format(Locale.US, {\lux\: %f}, lux)); } }JSInterface内部维护最新亮度值并通过带JavascriptInterface注解的getLux()暴露给网页。返回的是一个 JSON 字符串{lux: 123.45}网页端拿到后可直接JSON.parse使用。Locale.US保证浮点数格式化时统一使用.小数点避免在某些地区如德语区被格式化成逗号导致 JSON 解析失败。从源码结构看这里形成了两条互补的数据通路Push本示例主线onSensorChanged→postWebMessage→ 页面onmessage回调Pull辅助通道用户在页面点击链接触发LIGHT_SENSOR.getLux()→ 直接读取 Java 侧最新缓存值。网页侧 index.html接收消息与两种取数方式页面定义在 app/src/main/assets/index.html完整代码如下html head titleAndroid Light Sensor Demo/title script languagejavascript function update_lux(lux) { document.getElementById(lux).innerHTML lux; } function parse(json) { var result JSON.parse(json); update_lux(result.lux); } function pull() { parse(LIGHT_SENSOR.getLux()); } onmessage function (e) { parse(e.data); } /script /head body pba onClickpull()Light Level/a: /bspan idlux0.0/span lux/p /body /htmlonmessage接收 Java 推送的消息onmessage是WebMessage通道在页面侧的接收入口。当 Java 调用postWebMessage(new WebMessage(jsInterface.getLux()), Uri.EMPTY)时页面会触发onmessagee.data即消息正文——也就是{lux: 123.45}这样的 JSON 字符串。页面用parse()做JSON.parse后调用update_lux()更新页面上的显示文本。pull()点击触发的拉取页面里 Light Level 是一个a onClickpull()链接点击后执行pull()→parse(LIGHT_SENSOR.getLux())。这里的LIGHT_SENSOR就是 Java 侧addJavascriptInterface(jsInterface, LIGHT_SENSOR)注册的桥对象getLux()返回 Java 内存中最新缓存的亮度 JSON。于是网页可以在不需要 Java 主动推送的情况下随时取到最新传感器值。消息格式约定用 JSON 字符串封装数据两条通路push 与 pull最终汇入同一条数据格式链JSInterface.getLux()产出{lux: %f}形式的 JSON 字符串 → 网页parse()用JSON.parse解包 →update_lux(result.lux)渲染。这种原生侧序列化、网页侧解析的约定非常通用Java 只需保证输出合法 JSON 与统一Locale网页端即可用标准 JSON API 消费便于日后扩展为携带时间戳、精度等更多字段的结构化消息。对比之下evaluateJavascript/loadUrl(javascript:...)路径直接拼接update_lux(123.45)这样的函数调用表达式属于调用函数而非传递消息对页面实现有强耦合WebMessage则把数据与调用解耦页面只需声明onmessage即可是更面向未来也更能与 Service Worker、跨 frame 通信对齐的范式。同目录系列示例横向对比把四个示例放在一起看能更清楚地定位本示例的价值SensorPush同场景但没有 M 分支KITKAT 以下用evaluateJavascript、老版本用loadUrl(javascript:...)是调用 JS 函数路线的完整版SensorPull只展示 JS 回调 JavaaddJavascriptInterface无推送逻辑对应另一个方向SensorMessage本文加入WebMessage推送并保留 Pull 通道是消息化推送 桥接拉取的组合示范SensorPort在 M 上用createWebMessageChannel()创建WebMessagePort[]双向端口Java 持channel[0]、把channel[1]随首个WebMessage投递给页面页面onmessage里取e.ports[0]建立私有通道此后 Java 与页面可双向postMessage——这是WebMessage家族的进阶用法。构建运行与版本适配要点工程使用 Gradle 构建settings.gradle仅包含:app一个模块在 WebKit/SensorMessage 目录下执行 Gradle 同步后即可安装运行真机运行效果屏幕亮度变化用手遮挡/移开传感器区域时WebView 页面上的 lux 数值实时刷新若设备为 Android M 及以上走postWebMessage路径4.45.x 走evaluateJavascript更老设备走loadUrl(javascript:...)Uri.EMPTY表示不限制目标 origin如需要精确投递到特定域名的 frame可传入对应Uri。安全与工程实践提醒addJavascriptInterface是双刃剑从 Android 4.2API 17起只有标注JavascriptInterface的方法才会暴露给网页示例正是这样收敛暴露面的同时应只加载可信内容避免把任意网页注入到带桥的 WebView。混淆规则仓库自带的 proguard-project.txt 明确提醒——若启用 ProGuard必须保留 JS 接口类成员否则桥方法会被混淆掉导致网页调用失败#-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #}发布构建时请按实际包名com.commonsware.android.webkit.bridge下的JSInterface取消注释并替换fqcn.of.javascript.interface.for.webview。API 版本守卫postWebMessage属 M API代码中所有调用点都包在Build.VERSION.SDK_INT判断内避免在低版本设备上直接触发NoSuchMethodError。小结WebKit/SensorMessage用不到 200 行代码Java HTML 各一个文件讲清了Java 主动推送消息给网页这一 WebView 高级用法postWebMessage负责 M 时代的消息化投递evaluateJavascript与loadUrl(javascript:...)负责历史版本兼容addJavascriptInterface则反向提供网页主动拉取的通道。配合 SensorPush、SensorPull、SensorPort 三个兄弟示例你可以把 Java 与 WebView 网页之间的四种通信姿势一次集齐。赞分享示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载相关推荐cw-omnibus 源码精讲用 WebViewClient 接管 WebView 页面事件回调BrowserDemo3 示例实战cw omnibus 源码精讲用 WebViewClient 接管 WebView 页面事件回调BrowserDemo3 示例实战 本篇指南以 cw om示例工程教程cw-omnibus GeoWeb1 源码解析WebView 中 JavaScript 回调 Java 的 addJavascriptInterface 实战cw omnibus GeoWeb1 源码解析WebView 中 JavaScript 回调 Java 的 addJavascriptInterface 实战示例工程教程cw-omnibus WebKit/Browser2 实例解析Android WebView 基础集成实战指南cw omnibus WebKit/Browser2 实例解析Android WebView 基础集成实战指南 导读 本篇文章以 cw omnibus 仓库中示例工程教程上一篇Aerial自定义消息显示在屏保中添加个人座右铭下一篇standard-version 版本演进全解析从 v1.0.0 到 v9.5.0 的机制、配置与 CHANGELOG 实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考