液体核心项目
LiquidCore使Node.js虚拟机能够在Android和iOS应用中运行。它提供完整的运行时环境,包括虚拟文件系统。
LiquidCore还为Android开发者提供了一个方便的方法,让他们可以在应用内部执行原始JavaScript,就像iOS开发者可以用原生JavaScriptCore执行JavaScript一样。
安装
步骤1:确保项目已配置为使用npm
在您的项目根目录中,您必须有file package.json
文件。如果您还没有,可以通过以下方式创建它:
$ npm init
并按照向导中的步骤操作。
package.json
步骤2:安装LiquidCore并配置$ npm i liquidcore
$ npx liquidcore init
init
步骤将添加一些实用脚本和liquidcore
对象到您的package.json
文件中。它还将创建一个名为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查找文件时的一个特色。这些文件必须在安装时存在,否则即使后来创建,它们也不会在pod中可用。因此,在添加新的entry
之后,只需再次执行这部分即可。
$ npx liquidcore bundler --platform=ios
$ pod install
自动打包
0.7.0+版本中的一个新特性是能够在应用程序构建过程中自动打包JavaScript文件。这是在上述的gradle-config
和/或pod-config
步骤中进行的配置。打包选项存储在本地package.json
文件中的liquidcore
属性中。一个典型的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,并且你的应用程序是以调试模式构建的,它将首先尝试从服务器获取打包文件。如果服务器不可用,它将使用构建时自动打包的打包文件。在生产模式下,它将始终使用打包的打包文件。
使用
微服务API
微服务只是一个独立的Node.js实例,其启动代码通过URI引用。例如
Android Kotlin |
iPhone 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。package.json
中的liquidcore.entry
中引用的任何javascript入口文件将在每个构建时自动捆绑。默认情况下,初始化脚本创建并捆绑example.js
,但您可以轻松更改此。
一旦设置好Node.js环境,微服务就可以与宿主应用通信。这可以通过在构造函数中添加启动监听器来实现
Android Kotlin |
iPhone 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 |
iPhone 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
Play with 按照上面的说明,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-only(非原生)模块并将它们正常安装为require
。捆绑器将所有代码打包成一个单一文件。
API 文档
Android Javadocs (liquidcore-Nodejs)
Android Javadocs (liquidcore-V8)
授权协议
版权所有 (c) 2014 - 2020 LiquidPlayer
许可协议:MIT。详情见 LICENSE.md。