开发项目
本页面介绍如何一步步开发一个M8Test脚本项目。
简单项目
创建
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: "Php"
url: "https://github.com/m8test/language-release/releases/download/php-0.1.0/com.m8test.php-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"
其中指定了执行settings和init.settings文件的脚本语言名称、下载地址及版本。
创建
settings文件,每种语言所需的扩展名不同
_console.log("settings.kts run")
$console.log("settings.groovy run")
$console.log("settings.js run")
_console:log("settings.lua run")
<?php
$console->log("settings.php run");
_console.log("settings.py run")
$console.log("settings.rb run")
这里简单打印一句话。若包含子项目,可在此文件中配置。一个项目只需一个 settings 文件,子项目中无需该文件。
创建
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)。
创建
src目录用于存放脚本源码,新建上一步骤配置的入口文件
package com.example.script
_console.log("kotlin")
package com.example.script
$console.log("groovy")
$console.log("javascript")
_console:log("lua")
<?php
$console->log("php");
_console.log("python")
$console.log("ruby")
创建
res目录,并添加第3步配置的logo文件,即可完成最简单的项目。
带UI的项目
创建
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:用于调用脚本端注册的处理器
脚本端(
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:用于调用网页端已注册的处理器
09 December 2025