M8Test Help

开发项目

本页面介绍如何一步一步开发一个M8Test脚本项目.

简单项目

  1. 创建文件 settings.config.yaml 文件, 内容如下

language: name: "Kotlin" url: "https://github.com/m8test/language-release/releases/download/kotlin-0.1.0/com.m8test.kotlin-release_0.1.0.apk" version: "0.1.0"
language: name: "Groovy" url: "https://github.com/m8test/language-release/releases/download/groovy-0.1.0/com.m8test.groovy-release_0.1.0.apk" version: "0.1.0"
language: name: "Javascript" url: "https://github.com/m8test/language-release/releases/download/javascript-0.1.0/com.m8test.javascript.v8-release_0.1.0.apk" version: "0.1.0"
language: name: "Lua" url: "https://github.com/m8test/language-release/releases/download/lua-0.1.0/com.m8test.lua-release_0.1.0.apk" version: "0.1.0"
language: name: "Python" url: "https://github.com/m8test/language-release/releases/download/python-0.1.0/com.m8test.python-release_0.1.0.apk" version: "0.1.0"
language: name: "Ruby" url: "https://github.com/m8test/language-release/releases/download/ruby-0.1.0/com.m8test.ruby-release_0.1.0.apk" version: "0.1.0"

其中指定了执行settingsinit.settings文件脚本语言名称、下载地址以及版本

  1. 创建 settings 文件, 每种语言所需要的扩展名不一样

这里简单的打印一句话, 如果包含子项目可以在此文件中配置, 一个项目中只需要一个 settings 文件, 子项目中不需要 settings 文件

  1. 创建 build 文件, 每种语言所需要的扩展名不一样

_project.getPlugins().apply("kotlin") _project.setConfig { setEntry("com/example/script/primary.kts") setPackageName("com.example.script.kotlin") setLogo("logo.png") }
$project.getPlugins().apply("groovy") $project.setConfig { setEntry("com/example/script/primary.groovy") setPackageName("com.example.script.groovy") setLogo("logo.png") }
$project.getPlugins().apply("javascript") $project.setConfig(config => { config.setEntry("com/example/script/primary.js") config.setPackageName("com.example.script.js") config.setLogo("logo.png") })
_project:getPlugins():apply("lua") _project:setConfig(function(c) c:setEntry("com/example/script/primary.lua") c:setPackageName("com.example.script.lua") c:setLogo("logo.png") end)
<?php $project->getPlugins()->apply("php"); $project->setConfig(function ($config) { $config->setEntry("com/example/script/primary.php"); $config->setPackageName("com.example.script.php"); $config->setLogo("logo.png"); });
_project.getPlugins().apply("python") def configuration(config): config.setEntry("com/example/script/primary.py") config.setPackageName("com.example.script.py") config.setLogo("logo.png") _project.setConfig(configuration)
$project.getPlugins().apply("ruby") $project.setConfig { |config| config.setEntry("com/example/script/primary.rb") config.setPackageName("com.example.script.rb") config.setLogo("logo.png") }

其中配置语言插件以及项目基本信息(脚本入口文件、包名以及logo)

  1. 创建目录 src 用于存放脚本源码, 新建上一步骤配置的入口文件

  1. 创建 res 目录, 并创建第3步配置的logo文件即可实现最简单的项目

带UI的项目

  1. 创建 webview 目录用于存放ui相关文件, 实际上此目录就是存放静态网站的. 可以显示任意前端框架(vue,angular,react等) 编译后的产物, 也可以是简单的 index.html 文件, 这里演示简单的网站, 只有两个文件, 一个是 index.html, 一个是 index.js

index.html 内容如下, 非常简单, 仅仅是引用了 index.js 文件而已

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebView UI</title> <link href="https://fastly.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous"> <script src="https://fastly.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script> </head> <body> <button id="button" type="button" class="btn btn-primary">Primary</button> <script src="./index.js"></script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebView UI</title> <link href="https://fastly.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous"> <script src="https://fastly.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script> </head> <body> <button id="button" type="button" class="btn btn-primary">Primary</button> <script src="./index.js"></script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebView UI</title> <link href="https://fastly.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous"> <script src="https://fastly.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script> </head> <body> <button id="button" type="button" class="btn btn-primary">Primary</button> <script src="./index.js"></script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebView UI</title> <link href="https://fastly.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous"> <script src="https://fastly.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script> </head> <body> <button id="button" type="button" class="btn btn-primary">Primary</button> <script src="./index.js"></script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebView UI</title> <link href="https://fastly.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous"> <script src="https://fastly.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script> </head> <body> <button id="button" type="button" class="btn btn-primary">Primary</button> <script src="./index.js"></script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebView UI</title> <link href="https://fastly.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous"> <script src="https://fastly.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script> </head> <body> <button id="button" type="button" class="btn btn-primary">Primary</button> <script src="./index.js"></script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WebView UI</title> <link href="https://fastly.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous"> <script src="https://fastly.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script> </head> <body> <button id="button" type="button" class="btn btn-primary">Primary</button> <script src="./index.js"></script> </body> </html>

index.js 内容如下

(function () { function init() { // $webViewBridge.registerHandler() 用于设置供脚本端调用的处理器,第一个参数是处理器名,第二个参数是一个函数,会在处理器被调用时执行 $webViewBridge.registerHandler("functionForScriptToCall", function (data, responseCallback) { // data 为脚本端调用处理器时传递过来的参数 // responseCallback 用于返回处理结果到脚本端 if (responseCallback) { var responseData = "I am from Javascript " + data responseCallback(responseData) } }) } if (window.$webViewBridge) { init(); } else { document.addEventListener( 'WebViewBridgeReady', function () { //do your work here init(); }, false ); } })() document.getElementById("button").addEventListener("click", function (e) { // $webViewBridge.callHandler() 用于调用脚本端注册的处理器,参数一为处理器名,参数二为需要传递给处理器的参数,参数三为回调函数用于接收脚本端的返回值 $webViewBridge.callHandler("handlerForWebView", "params from js", function (p) { alert(p) }) })
(function () { function init() { // $webViewBridge.registerHandler() 用于设置供脚本端调用的处理器,第一个参数是处理器名,第二个参数是一个函数,会在处理器被调用时执行 $webViewBridge.registerHandler("functionForScriptToCall", function (data, responseCallback) { // data 为脚本端调用处理器时传递过来的参数 // responseCallback 用于返回处理结果到脚本端 if (responseCallback) { var responseData = "I am from Javascript " + data responseCallback(responseData) } }) } if (window.$webViewBridge) { init(); } else { document.addEventListener( 'WebViewBridgeReady', function () { //do your work here init(); }, false ); } })() document.getElementById("button").addEventListener("click", function (e) { // $webViewBridge.callHandler() 用于调用脚本端注册的处理器,参数一为处理器名,参数二为需要传递给处理器的参数,参数三为回调函数用于接收脚本端的返回值 $webViewBridge.callHandler("handlerForWebView", "params from js", function (p) { alert(p) }) })
(function () { function init() { // $webViewBridge.registerHandler() 用于设置供脚本端调用的处理器,第一个参数是处理器名,第二个参数是一个函数,会在处理器被调用时执行 $webViewBridge.registerHandler("functionForScriptToCall", function (data, responseCallback) { // data 为脚本端调用处理器时传递过来的参数 // responseCallback 用于返回处理结果到脚本端 if (responseCallback) { var responseData = "I am from Javascript " + data responseCallback(responseData) } }) } if (window.$webViewBridge) { init(); } else { document.addEventListener( 'WebViewBridgeReady', function () { //do your work here init(); }, false ); } })() document.getElementById("button").addEventListener("click", function (e) { // $webViewBridge.callHandler() 用于调用脚本端注册的处理器,参数一为处理器名,参数二为需要传递给处理器的参数,参数三为回调函数用于接收脚本端的返回值 $webViewBridge.callHandler("handlerForWebView", "params from js", function (p) { alert(p) }) })
(function () { function init() { // $webViewBridge.registerHandler() 用于设置供脚本端调用的处理器,第一个参数是处理器名,第二个参数是一个函数,会在处理器被调用时执行 $webViewBridge.registerHandler("functionForScriptToCall", function (data, responseCallback) { // data 为脚本端调用处理器时传递过来的参数 // responseCallback 用于返回处理结果到脚本端 if (responseCallback) { var responseData = "I am from Javascript " + data responseCallback(responseData) } }) } if (window.$webViewBridge) { init(); } else { document.addEventListener( 'WebViewBridgeReady', function () { //do your work here init(); }, false ); } })() document.getElementById("button").addEventListener("click", function (e) { // $webViewBridge.callHandler() 用于调用脚本端注册的处理器,参数一为处理器名,参数二为需要传递给处理器的参数,参数三为回调函数用于接收脚本端的返回值 $webViewBridge.callHandler("handlerForWebView", "params from js", function (p) { alert(p) }) })
(function () { function init() { // $webViewBridge.registerHandler() 用于设置供脚本端调用的处理器,第一个参数是处理器名,第二个参数是一个函数,会在处理器被调用时执行 $webViewBridge.registerHandler("functionForScriptToCall", function (data, responseCallback) { // data 为脚本端调用处理器时传递过来的参数 // responseCallback 用于返回处理结果到脚本端 if (responseCallback) { var responseData = "I am from Javascript " + data responseCallback(responseData) } }) } if (window.$webViewBridge) { init(); } else { document.addEventListener( 'WebViewBridgeReady', function () { //do your work here init(); }, false ); } })() document.getElementById("button").addEventListener("click", function (e) { // $webViewBridge.callHandler() 用于调用脚本端注册的处理器,参数一为处理器名,参数二为需要传递给处理器的参数,参数三为回调函数用于接收脚本端的返回值 $webViewBridge.callHandler("handlerForWebView", "params from js", function (p) { alert(p) }) })
(function () { function init() { // $webViewBridge.registerHandler() 用于设置供脚本端调用的处理器,第一个参数是处理器名,第二个参数是一个函数,会在处理器被调用时执行 $webViewBridge.registerHandler("functionForScriptToCall", function (data, responseCallback) { // data 为脚本端调用处理器时传递过来的参数 // responseCallback 用于返回处理结果到脚本端 if (responseCallback) { var responseData = "I am from Javascript " + data responseCallback(responseData) } }) } if (window.$webViewBridge) { init(); } else { document.addEventListener( 'WebViewBridgeReady', function () { //do your work here init(); }, false ); } })() document.getElementById("button").addEventListener("click", function (e) { // $webViewBridge.callHandler() 用于调用脚本端注册的处理器,参数一为处理器名,参数二为需要传递给处理器的参数,参数三为回调函数用于接收脚本端的返回值 $webViewBridge.callHandler("handlerForWebView", "params from js", function (p) { alert(p) }) })
(function () { function init() { // $webViewBridge.registerHandler() 用于设置供脚本端调用的处理器,第一个参数是处理器名,第二个参数是一个函数,会在处理器被调用时执行 $webViewBridge.registerHandler("functionForScriptToCall", function (data, responseCallback) { // data 为脚本端调用处理器时传递过来的参数 // responseCallback 用于返回处理结果到脚本端 if (responseCallback) { var responseData = "I am from Javascript " + data responseCallback(responseData) } }) } if (window.$webViewBridge) { init(); } else { document.addEventListener( 'WebViewBridgeReady', function () { //do your work here init(); }, false ); } })() document.getElementById("button").addEventListener("click", function (e) { // $webViewBridge.callHandler() 用于调用脚本端注册的处理器,参数一为处理器名,参数二为需要传递给处理器的参数,参数三为回调函数用于接收脚本端的返回值 $webViewBridge.callHandler("handlerForWebView", "params from js", function (p) { alert(p) }) })

需要注意以下几点

  • $webViewBridge.registerHandler: 在网页端注册处理器, 处理器注册后可以在脚本端调用已经注册的处理器

  • $webViewBridge.callHandler: 用于调用脚本端注册的处理器

  1. 脚本端(src )内容如下

package com.example.script // WebViewActivity.getBridge() 用于获取 WebViewBridge 对象 val bridge = _webView.getBridge() // WebViewBridge.registerHandler() 用于注册处理器供 WebView 端调用 bridge.registerHandler("handlerForWebView") { // it -> String // it 表示从 WebView 端传来的参数,如果没有则为 null val handledParams = it + " return from script" // WebViewBridge.callHandler() 用于调用 WebView 端注册的处理器 bridge.callHandler("functionForScriptToCall", handledParams) { i -> // it -> String // it 为 WebView 端js函数返回值 _console.log(i) } handledParams } // Activity.start() 用于启动Android系统的Activity _activity.start()
package com.example.script // WebViewActivity.getBridge() 用于获取 WebViewBridge 对象 def bridge = $webView.getBridge() // WebViewBridge.registerHandler() 用于注册处理器供 WebView 端调用 bridge.registerHandler("handlerForWebView") { // it -> String // it 表示从 WebView 端传来的参数,如果没有则为 null def handledParams = "$it return from script" // WebViewBridge.callHandler() 用于调用 WebView 端注册的处理器 bridge.callHandler("functionForScriptToCall", handledParams) { // it -> String // it 为 WebView 端js函数返回值 $console.log(it) } handledParams } // Activity.start() 用于启动Android系统的Activity $activity.start()
// WebViewActivity.getBridge() 用于获取 WebViewBridge 对象 let bridge = $webView.getBridge() // WebViewBridge.registerHandler() 用于注册处理器供 WebView 端调用 bridge.registerHandler("handlerForWebView", (it) => { // it -> String // it 表示从 WebView 端传来的参数,如果没有则为 null let handledParams = it + " return from script" // WebViewBridge.callHandler() 用于调用 WebView 端注册的处理器 bridge.callHandler("functionForScriptToCall", handledParams, (it) => { // it -> String // it 为 WebView 端js函数返回值 $console.log(it) }) return handledParams }) // Activity.start() 用于启动Android系统的Activity $activity.start()
-- WebViewActivity.getBridge() 用于获取 WebViewBridge 对象 local bridge = _webView:getBridge() -- WebViewBridge.registerHandler() 用于注册处理器供 WebView 端调用 bridge:registerHandler("handlerForWebView", function(it) -- it -> String -- it 表示从 WebView 端传来的参数,如果没有则为 nil local handledParams = it .. "return from script" -- WebViewBridge.callHandler() 用于调用 WebView 端注册的处理器 bridge:callHandler("functionForScriptToCall", handledParams, function(i) -- it -> String -- it 为 WebView 端js函数返回值 _console:log(i) end) return handledParams end) -- Activity.start() 用于启动Android系统的Activity _activity:start()
<?php // WebViewActivity.getBridge() 用于获取 WebViewBridge 对象 $bridge = $webView->getBridge(); // WebViewBridge.registerHandler() 用于注册处理器供 WebView 端调用 $bridge->registerHandler("handlerForWebView", function ($it) use ($console, $bridge) { // it -> String // it 表示从 WebView 端传来的参数,如果没有则为 nil $handledParams = $it . "return from script"; // WebViewBridge.callHandler() 用于调用 WebView 端注册的处理器 $bridge->callHandler("functionForScriptToCall", $handledParams, function ($i) use ($console) { // it -> String // it 为 WebView 端js函数返回值 $console->log($i); }); return $handledParams; }); // Activity.start() 用于启动Android系统的Activity $activity->start();
# WebViewActivity.getBridge() 用于获取 WebViewBridge 对象 bridge = _webView.getBridge() def fn1(it): # it -> String # it 表示从 WebView 端传来的参数,如果没有则为 null handledParams = it + " return from script" def fn2(i): # it -> String # it 为 WebView 端js函数返回值 _console.log(i) # WebViewBridge.callHandler() 用于调用 WebView 端注册的处理器 bridge.callHandler("functionForScriptToCall", handledParams, fn2) return handledParams # WebViewBridge.registerHandler() 用于注册处理器供 WebView 端调用 bridge.registerHandler("handlerForWebView", fn1) # Activity.start() 用于启动Android系统的Activity _activity.start()
# WebViewActivity.getBridge() 用于获取 WebViewBridge 对象 bridge = $webView.getBridge() # WebViewBridge.registerHandler() 用于注册处理器供 WebView 端调用 bridge.registerHandler("handlerForWebView") { |it| # it -> String # it 表示从 WebView 端传来的参数,如果没有则为 null handledParams = it + "return from script" # WebViewBridge.callHandler() 用于调用 WebView 端注册的处理器 bridge.callHandler("functionForScriptToCall", handledParams) { |i| # it -> String # it 为 WebView 端js函数返回值 $console.log(i) } handledParams } # Activity.start() 用于启动Android系统的Activity $activity.start()

需要注意以下几点

  • bridge.registerHandler: 用于在脚本端注册处理器, 注册的处理器可以在网页端调用

  • bridge.callHandler: 用于调用网页端已注册的处理器

Last modified: 08 August 2025