LiquidCore项目
LiquidCore使Node.js虚拟机能够在Android和iOS应用中运行。它提供完整的运行环境,包括虚拟文件系统。
LiquidCore还提供了一种方便的方法,让Android开发者可以在他们的应用中执行原生JavaScript,正如iOS开发者可以使用JavaScriptCore那样。
安装
npm
步骤 1:确保您的项目已配置为使用在您的项目根目录下,您必须有一个package.json
文件。如果您还没有,可以运行以下命令来创建它
$ npm init
并根据向导中的步骤操作。
package.json
步骤 2:安装LiquidCore并配置$ npm i liquidcore
$ npx liquidcore init
init
步骤将在您的package.json
文件中添加一些实用脚本和liquidcore
对象。它还会创建一个名为example.js
的示例服务,该服务将打包进您的应用中。您可以通过在package.json
中编辑liquidcore.entry
属性来更改/添加要打包的文件。
第3步:配置您的移动应用项目
Android |
iOS |
$ npx liquidcore gradle-config --module=<app> 其中 |
$ npx liquidcore pod-config --target=<target> --podfile=<podfile>
$ npx liquidcore bundler --platform=ios
$ pod install 其中 |
关于iOS的说明
LiquidCore需要使用Cocoapods,因此请确保您已设置好项目以使用Podfile
。**注意**:您的Podfile
必须包含use_frameworks!
指令。如果您尚未使用Cocoapods,可以创建一个如下的简单版本
platform :ios, '11.0'
use_frameworks!
target '<TARGET>' do
end
其中<TARGET>
是您的xcodeproj
的名称(不带.xcodeproj
扩展名)。
此外,每次您在package.json
中添加一个新的liquidcore.entry
点时,您必须首先运行捆绑器,然后再次运行pod install
。这是Cocoapods如何查找文件的奇怪之处。这些文件必须在安装时存在,否则即使稍后创建了这些文件,它们也不会在库中可用。因此,在添加新的entry
后,只需再次执行此部分即可
$ npx liquidcore bundler --platform=ios
$ pod install
自动打包
0.7.0+ 版本新增加的一项特性是能够在应用程序构建过程中自动打包 JavaScript 文件。这通过 gradle-config
和/或 pod-config
步骤进行配置。打包选项存储在位于 liquidcore
属性中的本地 package.json
文件中。一个典型的 liquidcore
对象可能看起来像这样
"liquidcore": {
"entry": [
"example.js",
"index.js"
],
"gradle_options": {
"module": "app"
},
"bundler_output": {
"android": "app/src/main/res/raw",
"ios": ".liquidcore/ios_bundle"
},
"bundler_options": {
"minify": false
},
"pod_options": {
"dev": true,
"target": "TestApp"
}
}
要包含一个新的打包文件,只需将入口点 JavaScript 文件放入 entry
数组属性。LiquidCore 将在构建过程中为每个 entry
生成一个打包文件。
如果你的应用程序有非标准配置,你可能需要更改这些值中的某些值。例如,Android 的 bundler_output
假定你的资源目录位于 <app-module>/src/main/res
。这是 Android Studio 的默认设置。如果你已经将其更改,则需要更新此属性。
打包是测试和打包你的 JavaScript 项目的便捷方式。打包器使用 Metro 将所有必需的 node 模块打包成单个文件,可将其作为您应用中的资源进行打包。如果你正在运行 Android 模拟器或 iOS 模拟器,你可以在您的开发机上运行本地服务器并通过在项目根目录中执行 npx liquidcore server
来热编辑您的 JavaScript 代码。如果你正在使用 Bundle
API(以下将描述),并且你的应用程序以调试模式编译,它将首先尝试从服务器获取打包文件。如果服务器不可用,它将使用构建时间自动打包的打包文件。在发行模式下,它将始终使用打包的打包文件。
使用方法
《MicroService》API
微型服务不过是一个独立的 Node.js 实例,其启动代码通过 URI 引用。例如
Android Kotlin |
iOS Swift |
val uri = MicroService.Bundle(androidContext, "example")
val service = MicroService(androidContext, uri)
service.start() |
import LiquidCore
...
let url = LCMicroService.bundle("example")
let service = LCMicroService(url: url)
service?.start() |
服务URI可以指向服务器URL或本地资源。对于LiquidCore自动与您的应用程序捆绑的服务,您可以使用MicroService.Bundle()
或LCMicroService.bundle()
方法生成正确的URI。在liquidcore.entry
中的package.json
中引用的任何javascript入口文件都会自动与每个构建捆绑在一起。默认情况下,初始化脚本创建和捆绑example.js
,但您可以轻松更改此设置。
在设置Node.js环境之后,微服务可以与宿主应用程序通信。这可以通过在构造函数中添加一个启动监听器来实现。
Android Kotlin |
iOS Swift |
val uri = MicroService.Bundle(androidContext, "example")
val startListener = MicroService.ServiceStartListener {
// .. The environment is live, but the startup
// JS code (from the URI) has not been executed yet.
}
val service = MicroService(androidContext, uri,
startListener)
service.start() |
遵守 let service = LCMicroService(url:url,
delegate:self)
service?.start()
...
func onStart(_ service: LCMicroService) {
// .. The environment is live, but the
// startup JS code (from the URI) has
// not been executed yet.
} |
微服务通过简单的EventEmitter
接口与宿主通信,该接口命名为LiquidCore
。例如,在您的JavaScript启动代码中
LiquidCore.emit('my_event', {foo: "hello, world", bar: 5, l337 : ['a', 'b'] })
在应用侧,宿主应用程序可以监听事件
Android Kotlin |
iOS Swift |
val listener = MicroService.EventListener {
service, event, payload ->
android.util.Log.i("Event:" + event,
payload.getString("foo"))
// logs: I/Event:my_event: hello, world
}
service.addEventListener("my_event", listener) |
遵守 service.addEventListener("my_event", listener:self)
...
func onEvent(_ service: LCMicroService, event: String,
payload: Any?) {
var p = (payload as! Dictionary<String,AnyObject>)
NSLog(format:"Event: %@: %@", args:event, p["foo"]);
// logs: Event:my_event: hello, world
} |
同样,微服务可以监听来自宿主的回调事件
Android Kotlin |
iOS Swift |
val payload = JSONObject()
payload.put("hallo", "die Weld")
service.emit("host_event", payload) |
var payload = ["hallo" : "die Weld"]
service.emitObject("host_event", object:payload) |
然后,在 JavaScript 中
LiquidCore.on('host_event', function(msg) {
console.log('Hallo, ' + msg.hallo)
})
LiquidCore 创建了一个便捷的虚拟文件系统,以便微服务实例不会无意或恶意地相互干扰,也不会与 Android/iOS 文件系统其余部分冲突。文件系统详细说明见此处。
example.js
玩转 按照上述说明操作后,LiquidCore 将自动捆绑一个名为 example.js
的文件,如下所示
const {LiquidCore} = require('liquidcore')
// A micro service will exit when it has nothing left to do. So to
// avoid a premature exit, set an indefinite timer. When we
// exit() later, the timer will get invalidated.
setInterval(()=>{}, 1000)
console.log('Hello, World!')
// Listen for a request from the host for the 'ping' event
LiquidCore.on( 'ping', () => {
// When we get the ping from the host, respond with "Hello, World!"
// and then exit.
LiquidCore.emit( 'pong', { message: 'Hello, World from LiquidCore!' } )
process.exit(0)
})
// Ok, we are all set up. Let the host know we are ready to talk
LiquidCore.emit( 'ready' )
以下是从应用中交互此 JavaScript 代码的示例。注意,Android 上的 hello_text
和 iOS 上的 textBox
是 UI 元素,其设置在此处未显示。
Android Kotlin |
iOS Swift |
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import org.liquidplayer.service.MicroService
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState:
Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val hello = findViewById<TextView>(
R.id.hello_text)
val readyListener = MicroService.EventListener {
service, _, _ -> service.emit("ping")
}
val pongListener = MicroService.EventListener {
_, _, jsonObject ->
val message = jsonObject.getString("message")
runOnUiThread { hello.text = message }
}
val startListener =
MicroService.ServiceStartListener{
service ->
service.addEventListener("ready", readyListener)
service.addEventListener("pong", pongListener)
}
val uri = MicroService.Bundle(this, "example")
val service = MicroService(this, uri,
startListener)
service.start()
}
} |
import UIKit
import LiquidCore
class ViewController: UIViewController,
LCMicroServiceDelegate,
LCMicroServiceEventListener {
@IBOutlet weak var textBox: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let url = LCMicroService.bundle("example")
let service = LCMicroService(url: url,
delegate: self)
service?.start()
}
func onStart(_ service: LCMicroService) {
service.addEventListener("ready",listener: self)
service.addEventListener("pong", listener: self)
}
func onEvent(_ service: LCMicroService,
event: String,
payload: Any?) {
if event == "ready" {
service.emit("ping")
} else if event == "pong" {
let p = (payload as! Dictionary<String,AnyObject>)
let message = p["message"] as! String
DispatchQueue.main.async {
self.textBox.text = message
}
}
}
} |
您可以用这个作为创建自己的服务的指南。您可以使用 npm install
安装大多数仅使用 JavaScript(非本地)的模块,并且可以作为正常安装它们。打包器会将所有代码打包成一个文件。
API 文档
Android Javadocs (liquidcore-Nodejs)
Android Javadocs (liquidcore-V8)
许可证
版权所有 (c) 2014 - 2020 LiquidPlayer
遵照 MIT 许可证分发。详情请见 LICENSE.md。