使用 Web 编写界面(WebView)
2022/10/22大约 4 分钟
提示
Auto.js Pro 软件内也带有使用 Web 编写界面的示例,参见 示例 → 复杂界面 → Vue2。
本文将介绍如何在 Auto.js Pro 的 UI 中嵌入 WebView,加载网页内容(远程或本地),并实现 Auto.js(脚本侧)与页面 JavaScript 的双向通信。
什么时候适合用 WebView 写 UI
适合的场景:
- 你已经有一套 Web 前端(Vue/React 等),希望复用到 Auto.js 中。
- 需要更复杂的布局/动画,想直接使用 HTML/CSS。
- 需要富文本渲染与交互(例如图文混排、复杂表单、组件化 UI)。
不太适合的场景:
- UI 要求极致轻量(WebView 有明显的内存/启动开销)。
- 依赖大量原生控件行为(如复杂列表、输入法边界行为、原生无障碍交互等)。
最小示例:在 UI 中嵌入 WebView
WebView 是 Android 的一个 View。在 Auto.js 的 UI XML 中,有的运行环境支持 <webview /> 标签;如果不支持,可以直接使用 android.webkit.WebView 类名。
示例(推荐先试这个):
"ui";
$ui.layout(
<vertical padding="16dp">
<text text="WebView demo" textSize="18sp" />
<webview id="wv" h="*" w="*" />
</vertical>,
);
// 需要在 UI 线程操作 View
ui.post(() => {
ui.wv.loadUrl("https://example.com");
});如果你的环境不识别 <webview />,改成 Android 类名:
"ui";
$ui.layout(
<vertical padding="16dp">
<android.webkit.WebView id="wv" h="*" w="*" />
</vertical>,
);加载本地 HTML
常见有两种方式:
方式 A:从存储加载 file://
"ui";
const htmlPath = "/sdcard/scripts/webview/index.html";
$ui.layout(<android.webkit.WebView id="wv" h="*" w="*" />);
ui.post(() => {
ui.wv.loadUrl("file://" + htmlPath);
});注意:
- 某些 Android 版本 / WebView 配置下,
file://访问可能受限。 - SPA(Vue/React)通常有很多相对路径资源(JS/CSS/图片),如果 base 路径不对可能加载失败。
方式 B:内联 HTML(loadDataWithBaseURL)
适合快速原型与示例。
"ui";
$ui.layout(<android.webkit.WebView id="wv" h="*" w="*" />);
const html = `<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Auto.js WebView</title>
<style>body{font-family:sans-serif;padding:16px}</style>
</head>
<body>
<h3>Hello WebView</h3>
<button onclick="document.body.insertAdjacentHTML('beforeend','<p>clicked</p>')">Click</button>
</body>
</html>`;
ui.post(() => {
// baseUrl 可为 null,也可以给一个“假域名”用于相对路径与同源策略,例如 https://app.local/
ui.wv.loadDataWithBaseURL(
"https://app.local/",
html,
"text/html",
"utf-8",
null,
);
});开启 JS / 存储 / 调试
WebView 默认比较“保守”,很多页面需要 JavaScript 与 DOMStorage。
"ui";
$ui.layout(<android.webkit.WebView id="wv" h="*" w="*" />);
ui.post(() => {
const settings = ui.wv.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setAllowFileAccess(true);
});Chrome DevTools 调试(可选):
importClass(android.webkit.WebView);
WebView.setWebContentsDebuggingEnabled(true);然后在电脑 Chrome 打开 chrome://inspect 进行查看与调试。
监听加载事件(WebViewClient)
想要拦截跳转、观察页面加载状态,可以设置 WebViewClient:
"ui";
importClass(android.webkit.WebViewClient);
$ui.layout(<android.webkit.WebView id="wv" h="*" w="*" />);
ui.post(() => {
ui.wv.setWebViewClient(new JavaAdapter(WebViewClient, {
shouldOverrideUrlLoading(view, request) {
try {
const url = request.getUrl().toString();
console.log("navigating:", url);
} catch (e) {}
return false; // 返回 false 表示交给 WebView 正常加载
},
onPageStarted(view, url, favicon) {
console.log("page started:", url);
},
onPageFinished(view, url) {
console.log("page finished:", url);
},
}));
ui.wv.loadUrl("https://example.com");
});JS ↔ Auto.js 通信
通信方式很多,建议根据安全需求选择。
方案 A(推荐):自定义 URL Scheme
页面通过跳转到 autojs://bridge?... 发送消息,Auto.js 在 shouldOverrideUrlLoading 里拦截并解析。
Auto.js 侧示例:
"ui";
importClass(android.webkit.WebViewClient);
$ui.layout(<android.webkit.WebView id="wv" h="*" w="*" />);
function decodeQuery(url) {
const i = url.indexOf("?");
if (i < 0) return {};
const q = url.slice(i + 1);
const obj = {};
q.split("&").forEach(kv => {
const [k, v] = kv.split("=");
obj[decodeURIComponent(k)] = decodeURIComponent(v || "");
});
return obj;
}
ui.post(() => {
const settings = ui.wv.getSettings();
settings.setJavaScriptEnabled(true);
ui.wv.setWebViewClient(new JavaAdapter(WebViewClient, {
shouldOverrideUrlLoading(view, request) {
const url = request.getUrl().toString();
if (url.startsWith("autojs://bridge")) {
const q = decodeQuery(url);
console.log("bridge message:", q);
return true; // 已处理,阻止 WebView 继续加载
}
return false;
},
}));
ui.wv.loadDataWithBaseURL(
"https://app.local/",
`<!doctype html><html><body>
<button onclick="location.href='autojs://bridge?type=ping&msg='+encodeURIComponent('hello')">Send</button>
</body></html>`,
"text/html",
"utf-8",
null,
);
});推荐理由:
- 不需要把 Java 对象暴露到页面里。
- 便于做来源/参数校验,安全边界更清晰。
方案 B:addJavascriptInterface(强大,但需要谨慎)
addJavascriptInterface 会把 Java 对象暴露给页面 JS 使用,攻击面更大。不要对不可信远程页面使用。
"ui";
$ui.layout(<android.webkit.WebView id="wv" h="*" w="*" />);
const Bridge = JavaAdapter(java.lang.Object, {
postMessage(msg) {
console.log("from page:", msg);
}
});
ui.post(() => {
const settings = ui.wv.getSettings();
settings.setJavaScriptEnabled(true);
ui.wv.addJavascriptInterface(Bridge, "AutoJs");
ui.wv.loadDataWithBaseURL(
"https://app.local/",
`<!doctype html><html><body>
<button onclick="AutoJs.postMessage('hello from web')">Send</button>
</body></html>`,
"text/html",
"utf-8",
null,
);
});在 WebView 里运行 Vue/React
常见落地方式:
- 打包 SPA 静态资源到一个目录,通过
file://加载index.html(或配合本地 HTTP 服务)。 - 启动本地 HTTP 服务(对资产多的 SPA 更友好)。务必只监听 localhost,避免暴露到局域网。
实践建议:
- 需要离线能力时,尽量不要依赖远程 CDN。
- 开发调试建议开启 sourcemap 与 WebView 调试。
- 处理好刷新/返回等导航逻辑(必要时自行实现返回栈)。
安全注意事项
- 除非页面完全本地,否则默认把 WebView 内容当作“不可信”。
- 不要把高权限能力暴露给远程页面。
- 如果必须加载远程页面,优先使用 URL Scheme 方案并做严格校验;避免
addJavascriptInterface。
相关链接
$uiAPI 参考:ui/api.md- UI 规范:
ui/guidelines.md
