KernelSU 模块 WebUI 开发指南:webroot 目录结构与 JavaScript API 实战
【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU
KernelSU 的模块机制不止于在开机阶段执行脚本、修改系统文件:它允许模块作者用任意 Web 技术(HTML + CSS + JavaScript)编写可视化页面,由 KernelSU 管理器通过 WebView 加载展示,并通过一组系统 API 实现与 Android 系统的深度交互(执行 shell 命令、查询应用信息、控制全屏等)。本文以 website/docs/pt_BR/guide/module-webui.md 为骨架,结合仓库中 JS 库与管理器源码,讲解webroot目录规范、kernelsunpm 库的全部 API、底层桥接原理与实战注意事项。
什么是模块 WebUI
除执行启动脚本与修改系统文件外,KernelSU 模块还可向用户展示图形界面并与用户直接交互。模块作者可以使用任何 Web 技术编写页面,KernelSU 管理器(manager app)通过内置 WebView 渲染这些页面,同时向页面暴露一组系统 API——例如执行 shell 命令、读取/修改系统属性等。
从管理器源码 WebViewHelper.kt 可以看到,模块页面只有在满足特定条件时才会被加载:模块必须声明了 WebUI 能力(hasWebUi)、处于启用状态(enabled)、且未处于更新(update)或待移除(remove)状态。这保证了只有"健康"的模块才能弹出界面,避免半卸载状态下的模块继续运行页面。
WebUI 的入口是WebUIActivity,管理器通过 intent 携带id查询参数定位模块(见 WebUIActivity.kt),加载流程由prepareWebView完成。
webroot目录:模块页面的根目录
模块的 Web 资源文件必须放在模块根目录下的webroot子目录中,并且必须存在一个名为index.html的文件,它是模块页面的入口。包含 Web 界面的最简模块结构如下:
❯ tree . . |-- module.prop `-- webroot `-- index.html如果页面还包含 CSS 与 JavaScript 文件,也需要一并放在该目录下。KernelSU 管理器加载页面时,默认入口正是https://mui.kernelsu.org/index.html(见 WebUIScreen.kt),该域名由管理器内部的WebViewAssetLoader映射到模块的webroot目录。
::: warning 重要警告 安装模块时,KernelSU 会自动设置webroot目录的权限与 SELinux 上下文。如果你不清楚自己在做什么,不要自行修改该目录的权限,否则可能导致 WebView 无法读取页面资源,甚至引入安全问题。 :::
webroot常量定义在 ksud 侧 defs.rs(MODULE_WEB_DIR: &str = "webroot"),而管理器读取的实际路径为/data/adb/modules/<moduleId>/webroot(见 WebViewHelper.kt)。模块作者无需关心安装细节,只需保证目录名与入口文件名符合规范。
资源读取与安全边界
管理器并非直接把webroot当普通文件目录读取,而是通过 SuFilePathHandler.java 以 root shell(SuFile/SuFileInputStream)方式读取模块文件。该处理器内置了安全防护:
- 目录穿越防护:
getCanonicalFileIfChild只允许访问webroot规范路径下的子文件,请求路径逃逸出挂载目录时会返回 404(SuFilePathHandler.java); - 敏感目录黑名单:
/data/data与/data/system被列为禁止暴露目录(SuFilePathHandler.java); - MIME 推断:根据文件扩展名猜测 MIME 类型,无法识别时回退为
text/plain(SuFilePathHandler.java),.svgz压缩资源会被自动解压。
此外,webroot内还内置了两个虚拟资源:internal/insets.css(窗口安全区 CSS 变量)与internal/colors.css(跟随管理器主题的 Monet 动态取色 CSS),页面可通过相对路径引用它们来实现沉浸式与主题联动效果。
JavaScript API:kernelsu npm 库
如果只是纯展示页面,它就像普通网页一样工作。但 KernelSU 真正的亮点在于为页面提供了一套系统 API,允许实现模块专属功能。KernelSU 提供官方 JavaScript 库,发布在 npm 上(包名kernelsu,当前仓库内版本为 3.0.2,见 js/package.json),可直接在页面 JavaScript 中使用。
例如,执行一条 shell 命令获取机型配置或修改属性:
import { exec } from 'kernelsu'; const { errno, stdout } = exec("getprop ro.product.model");还可以让页面全屏显示、弹出 Toast 提示等。JS 库的完整实现位于 js/index.js,类型声明位于 js/index.d.ts,下面按功能逐一展开。
exec:执行 shell 命令
exec用于一次性执行 shell 命令,返回 Promise,结果包含errno(退出码)、stdout(标准输出)与stderr(标准错误):
import { exec } from 'kernelsu'; // 简单调用 const { errno, stdout } = await exec("getprop ro.product.model"); // 携带选项:指定工作目录与临时环境变量 const result = await exec("ls -l", { cwd: "/data/adb/modules", env: { "MY_VAR": "hello" } }); if (result.errno === 0) { console.log(result.stdout); }ExecOptions支持cwd(工作目录)与env(环境变量字典),见 index.d.ts。从底层实现看,管理器会在命令前拼接cd <cwd>;与若干export <key>=<value>;前缀后,以 root shell 执行(见 WebViewInterface.kt)。JS 侧通过window上注册临时回调函数接收结果,回调名由时间戳与计数器生成,避免并发调用冲突(见 js/index.js)。
spawn:流式执行长任务
对于需要持续输出或长时间运行的任务(如安装脚本、下载进度),应使用spawn。它返回一个ChildProcess对象,支持stdout/stderr的data事件流式接收输出,以及exit/error事件:
import { spawn } from 'kernelsu'; const child = spawn("sh", ["-c", "for i in 1 2 3; do echo $i; sleep 1; done"]); child.stdout.on('data', (data) => { console.log("[stdout]", data); }); child.stderr.on('data', (data) => { console.error("[stderr]", data); }); child.on('exit', (code) => { console.log("exit code:", code); }); child.on('error', (err) => { console.error("spawn error:", err); });spawn支持四种调用签名(command、command + args、command + options、command + args + options),args为字符串数组,options同样支持cwd与env(见 index.d.ts)。底层实现中,管理器创建持久 root shell,通过CallbackList把逐行输出实时回传给 JS 回调对象,任务结束后统一关闭 shell(见 WebViewInterface.kt)。
界面控制与信息查询 API
除执行命令外,JS 库还提供以下方法(完整签名见 index.d.ts):
| API | 说明 | 底层实现 |
|---|---|---|
fullScreen(isFullScreen) | 切换页面全屏显示 | 隐藏/显示系统状态栏与导航栏(WebViewInterface.kt) |
enableEdgeToEdge(enable) | 启用/禁用边到边(沉浸式)布局 | 控制 insets 计算与页面 CSS 注入(WebViewInterface.kt) |
toast(message) | 弹出 Toast 提示 | 经主线程 Handler 弹出系统 Toast(WebViewInterface.kt) |
moduleInfo() | 获取当前模块信息(id、name、version 等) | 遍历已安装模块,按模块 id 匹配后返回 JSON 字符串(WebViewInterface.kt) |
listPackages(type) | 列出应用包名,type可为"system"/"user",其他值返回全部 | 基于SuperUserViewModel.apps按系统/用户应用过滤并排序(WebViewInterface.kt) |
getPackagesInfo(packages) | 批量查询应用详情(包名、版本名、版本号、label、是否系统应用、uid) | 返回PackagesInfo[],找不到的应用返回error字段(WebViewInterface.kt) |
exit() | 关闭当前模块 WebUI 页面 | 触发WebUIEvent.Close,Activity 调用finish()(WebViewInterface.kt) |
例如,模块页面可以列出用户安装的应用并展示其图标:
import { listPackages, getPackagesInfo } from 'kernelsu'; const userApps = listPackages("user"); const infos = getPackagesInfo(userApps.slice(0, 5)); for (const info of infos) { console.log(info.appLabel, info.packageName, info.versionName); }管理器还为页面提供了应用图标加载能力:页面中引用ksu://icon/<packageName>形式的图片 URL 时,WebViewClient.shouldInterceptRequest会返回对应应用的 PNG 图标(见 WebViewHelper.kt),可用于自建应用列表 UI。
JavaScript 桥接原理
JS 库与原生层的桥接依赖@JavascriptInterface注入到 WebView 的ksu全局对象(见 WebViewHelper.kt)。以exec为例,完整调用链为:
- JS 侧调用
exec(cmd, options),生成唯一回调函数名并挂到window上; - JS 侧调用原生
ksu.exec(cmd, JSON.stringify(options), callbackFuncName); - 原生侧拼接
cwd/env前缀后以 root shell 同步执行,将errno、stdout、stderr以javascript:URL 注入回 WebView,调用注册好的回调函数; - JS 侧 Promise 兑现,清理临时回调(js/index.js)。
spawn的桥接类似,但回调对象以ChildProcess形式常驻window,stdout/stderr数据与exit/error事件通过多次javascript:注入逐条推送。
一些实用提示
localStorage 的局限性
你可以在页面中正常使用localStorage存储数据,但请牢记:管理器 App 被卸载时这些数据会随之丢失。如果模块需要持久化存储,必须手动把数据写入模块目录或其他稳定位置(例如通过exec写文件到/data/adb/modules/<moduleId>/下),并在页面加载时读取恢复。
打包工具建议
对于简单页面,官方推荐使用 parceljs 进行打包——它零配置、开箱即用,非常适合快速开发。示例:
# 初始化项目并安装 kernelsu 与 parcel npm install kernelsu npm install --save-dev parcel # 开发 / 构建 npx parcel build index.html --dist-dir dist构建产物会输出到dist目录,将该目录内容(或重命名后的webroot)放进模块即可。当然,如果你习惯 Vite、Rollup、Webpack 等工具,或本身是前端专家,完全可以自由选择——只需保证最终产物入口为webroot/index.html。
沉浸式界面与主题适配
页面可使用enableEdgeToEdge(true)开启沉浸式布局,此时管理器会把系统栏 insets 以 CSS 形式注入页面(internal/insets.css),页面可据此为状态栏/导航栏区域预留 padding。同时可引用internal/colors.css获取与管理器 Material You 动态取色一致的 CSS 变量,让页面在明暗主题下都能与系统观感统一(见 SuFilePathHandler.java)。
一个完整的最小示例
结合以上内容,一个带 UI 的完整模块结构如下:
my-module/ |-- module.prop `-- webroot |-- index.html |-- style.css `-- app.jsindex.html引入本地样式与脚本:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="style.css"> </head> <body> <h1 id="device"></h1> <button id="fullscreen">全屏切换</button> <script type="module" src="app.js"></script> </body> </html>app.js使用kernelsu库调用系统能力:
import { exec, fullScreen, toast } from 'kernelsu'; // 读取设备型号并展示 const { errno, stdout } = await exec("getprop ro.product.model"); document.getElementById("device").textContent = errno === 0 ? stdout : "获取失败"; // 全屏切换与 Toast let isFull = false; document.getElementById("fullscreen").addEventListener("click", () => { isFull = !isFull; fullScreen(isFull); toast(isFull ? "已进入全屏" : "已退出全屏"); });总结
KernelSU 模块 WebUI 为模块作者提供了标准的webroot目录规范与功能完整的 JavaScript API 库(exec/spawn/fullScreen/toast/moduleInfo/listPackages/getPackagesInfo/exit等),配合管理器侧 root shell 桥接与安全的文件读取机制,可以轻松实现"模块即应用"的交互体验。开发时注意三点即可:入口必须是webroot/index.html、持久数据不要依赖localStorage、目录权限交给 KernelSU 自动管理。更多 API 细节可查阅仓库中的 js/index.js 与 js/index.d.ts,管理器端完整实现可参考 WebViewInterface.kt。
【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考