使用 Kanzi Android Service 进行开发¶
Kanzi Android Service 使您能够在设备上从多个应用程序使用同一个 Kanzi Android framework (droidfw) 实例。实现方式是:在一个应用程序的 Android 服务中托管 Kanzi Android framework (droidfw),然后所有客户端应用程序通过 Android IPC 机制使用它。
这带来以下能力:
Shared libraries。通过在客户端应用程序之间复用同一套 Kanzi 库来优化静态内存使用。
Shared runtime and resources。通过在客户端应用程序之间共享相同的 Kanzi Engine 和资源来优化运行时内存和 GPU 使用。
Improved application startup time。通过在后台预加载 Kanzi Engine 和资源来缩短启动时间。
Cross-application transition animations。使用共享的 Kanzi 资源,在不同应用程序之间实现无缝的迁移动画。
有关如何使用 Kanzi Android Service 的示例,请参阅 Android 服务示例。
备注
Kanzi Android Service 是一项实验性功能,您的反馈对我们非常重要。要提交反馈,请使用此 反馈表单。
架构详解¶
服务宿主应用程序¶
此应用程序通过 kzservice 库托管 Kanzi Android Service。该服务负责初始化 Kanzi Android framework (droidfw),并通过它初始化 Kanzi 运行时。宿主应用程序用于打包您的所有 Kanzi 资产、Kanzi 原生库和 Kanzi Engine 插件。
客户端应用程序¶
这些是同一系统上使用服务应用程序所提供 Kanzi 功能的任意数量的应用程序。它们使用 kzclient 库中的视图和可组合项类型访问 Kanzi。
跨应用程序迁移动画¶
客户端可以请求服务使用共享的 Kanzi 资源,在不同应用程序之间执行无缝的迁移动画。其工作原理是:服务创建一个显示在所有其他应用程序之上的叠加窗口,并在其中渲染迁移动画。动画结束后,该叠加窗口会被隐藏。
使用入门¶
您可以使用 Package Manager 提供的应用程序模板创建 Kanzi Android Service 工程。请参阅 安装 Package Manager。
开始之前,请确保您的环境满足 Kanzi Android Service 的要求。请参阅 Kanzi Android Service。
要在 Kanzi Studio 中创建 Kanzi Android Service 工程:
在 New Project 窗口中,将 Template 设置为 Kanzi Android Service based Application Template。
单击 Create。
Kanzi 会创建一个 Kanzi Studio 工程和两个 Android Studio 应用程序工程:
位于
<MyProject>Applicationandroid_service目录中的MyProjectHost服务宿主应用程序。位于
<MyProject>MyProjectClient目录中的MyProjectClient应用程序。
使用 Client API¶
这是您在客户端应用程序中用于配置服务应用程序中的 Kanzi Engine 实例并与之通信的 API。
View 和 Composable 类型¶
您可以将 Kanzi 内容添加到 XML 布局或 Composable 中。
在 XML 布局中,对于基于 SurfaceView 的渲染,添加
<com.rightware.kanzi.service.KanziServiceSurfaceView
android:id="@+id/clientred"
app:clientId="clientred"
app:kzbPathList="clientred.kzb"
app:startupPrefabUrl="kzb://clientred/StartupPrefab"
app:serviceHostPackage="com.example.host"
app:serviceIntentAction="com.example.host.action.LAUNCH_SERVICE"
/>
对于基于 TextureView 的渲染,添加
<com.rightware.kanzi.service.KanziServiceTextureView
android:id="@+id/clientred"
app:clientId="clientred"
app:kzbPathList="clientred.kzb"
app:startupPrefabUrl="kzb://clientred/StartupPrefab"
app:serviceHostPackage="com.example.host"
app:serviceIntentAction="com.example.host.action.LAUNCH_SERVICE"
/>
要了解基于 SurfaceView 的视图与基于 TextureView 的视图之间的区别,请参阅 添加 Kanzi 视图。
或者在您的 Composable 中添加:
KanziServiceComposable(
modifier = Modifier,
request = Request(
clientId = "clientblue",
kzbPathList = "clientblue.kzb",
startupPrefabUrl = "kzb://clientblue/StartupPrefab",
),
serviceHostPackage = "com.example.host",
serviceIntentAction = "com.example.host.action.LAUNCH_SERVICE"
)
KanziServiceComposable 基于 KanziServiceSurfaceView,并且还接受以下可选参数:transparency、focusableInTouchMode 和 listener。
View 和 Composable 类型接受以下 XML 特性:
特性 (Attribute) |
格式 (Format) |
描述 (Description) |
|---|---|---|
|
string |
每个客户端必须提供的唯一标识符。 |
|
string |
要为此客户端加载的 .kzb 或 .kzb.cfg 文件的逗号分隔列表。 |
|
string |
客户端加载时实例化到客户端表面上的预设件的 URL。 |
|
string |
托管 Kanzi Android Service 的应用程序的 Android 包名。 |
|
string |
Kanzi Android Service 用于将客户端应用程序与服务绑定的 Intent 动作。 |
|
颜色 |
用于清除表面的颜色。默认值: |
|
BOOLEAN |
启用清除表面。默认值: |
|
BOOLEAN |
启用内置的旋钮输入处理。设置后,Kanzi 会处理旋钮输入事件并将其转换为 Kanzi 按键事件。默认值: |
|
BOOLEAN |
启用透明度支持。默认值: |
确保 serviceHostPackage 和 serviceIntentAction 与 Service 宿主应用程序清单文件中声明的 applicationId 和 Intent 动作完全一致。
KanziServiceClientListener¶
KanziServiceClientListener 是用于接收客户端生命周期事件通知的接口。所有回调都有默认的空实现,因此您只需重写您关注的回调。
回调 |
描述 (Description) |
|---|---|
|
在 Kanzi Android Service 已连接且客户端可供使用时调用。 |
|
在客户端视图附加到窗口时调用。 |
|
在客户端视图从其窗口分离时调用。 |
|
在渲染表面已创建且可用时调用。 |
|
在渲染表面即将被销毁时调用。 |
|
在渲染表面尺寸发生变化时调用。 |
要在视图上设置监听器:
val kanziView = findViewById<KanziServiceSurfaceView>(R.id.clientred)
kanziView.listener = object : KanziServiceClientListener {
override fun onServiceConnected(client: KanziServiceClient) {
// Service is ready, interact with Kanzi
}
}
备注
必须在建立服务连接之前或在将视图附加到窗口之前设置监听器。如果在服务已连接之后设置监听器,会导致错过回调。
数据源 setter¶
在基于视图的应用程序中,您可以按如下方式获取 KanziServiceClient 引用:
val clientX = findViewById(R.id.clientred) as KanziServiceClient
在基于 Compose 的应用程序中,按如下方式获取:
var kanziServiceClient: KanziServiceClient? by remember { mutableStateOf(null) }
KanziServiceComposable(
...
listener = object : KanziServiceClientListener {
override fun onServiceConnected(client: KanziServiceClient) {
kanziServiceClient = client
}
},
)
通过该引用,您可以访问 setter pushDataInt、pushDataString、pushDataReal 和 pushDataBoolean。例如:
kanziServiceClient?.pushDataInt(
"kzb://launcher/Data Sources/Scene Data source",
"MainMenuScene",
MainMenuScenes.HOME.value
)
您需要提供数据源的 KZB URL、数据对象的路径以及要推送的值。
手动分离客户端¶
默认情况下,当 activity 停止时,客户端应用程序不会显式地从服务分离。因此,服务会继续运行关联的 Kanzi Engine 实例,并保留为该客户端分配的资源。
要显式分离,请在 activity 的生命周期方法中对 KanziServiceClient 实例调用 handleClientAttached 和 handleClientDetached。
override fun onStop() {
super.onStop()
kanziServiceClient?.handleClientDetached()
}
override fun onStart() {
super.onStart()
kanziServiceClient?.handleClientAttached()
}
备注
手动分离与无缝迁移功能不兼容。如果您使用跨应用程序迁移,请勿调用这些方法。
处理方向更改¶
当设备方向更改时,通知 Kanzi Android Service,以便它相应地更新 Kanzi 渲染:
kanziServiceClient?.handleOrientationChange(newOrientation)
将 newOrientation 设置为来自 Android Configuration 的新设备方向值。
处理可见性更改¶
当客户端视图的可见性更改时,通知 Kanzi Android Service,以便它优化渲染:
kanziServiceClient?.handleVisibilityChange(isVisible)
视图可见时将 isVisible 设置为 true,不可见时设置为 false。
使用 Service API¶
这是您在服务宿主应用程序中通过 Kanzi Java API 初始化和配置 Kanzi Android framework (droidfw) 时使用的 API:
KanziServiceRuntime 是一个引用计数的单例,您可以通过调用 KanziServiceRuntime.acquire(context) 获取它。将返回的 Reference 作为成员存储在您的 Application 子类中,以便在服务启动之前保持运行时存活。当最后一个 Reference 关闭时,运行时会关闭。
要注册 Kanzi Engine Java 插件,请获取
KanziServiceRuntime,并将其domain与 Kanzi Java API 一起使用。例如:private lateinit var runtimeRef: KanziServiceRuntime.Reference override fun onCreate() { super.onCreate() runtimeRef = KanziServiceRuntime.acquire(this) val domain = requireNotNull(runtimeRef.get().domain) { "KanziServiceRuntime.domain must not be null after acquire()" } domain.registerPlugin(MyJavaPlugin()) }
您可以实现
KanziServiceViewAdapterListener,以在服务宿主应用程序中监听与客户端应用程序关联的KanziViewAdapter的状态更改。例如,使用
KanziServiceViewAdapterListener.onPrefabLoaded回调来访问和修改您配置的启动预设件实例化的节点树。override fun onCreate() { super.onCreate() KanziServiceRuntime.addViewAdapterListener(clientId, object : KanziServiceViewAdapterListener { override fun onAttachedToWindow( clientId: String, viewAdapter: KanziViewAdapter, domain: Domain ) { // Look up a child node starting from the root. val text = viewAdapter.root.lookupNode<TextBox2D>("#Text") // Set properties on a node. text.setProperty(TextBox2D.TextProperty, "Hello world!") } }) }
预加载客户端预设件¶
您可以在服务宿主应用程序中预加载客户端预设件,这样当客户端应用程序启动时,Kanzi 内容会立即可用,无需等待预设件加载。
要预加载客户端预设件,请在您的 Application 子类的 onCreate 方法中获取 KanziServiceRuntime,并调用 preloadClientPrefab:
private lateinit var runtimeRef: KanziServiceRuntime.Reference
override fun onCreate() {
super.onCreate()
runtimeRef = KanziServiceRuntime.acquire(this)
runtimeRef.get().preloadClientPrefab(
"clientred",
arrayOf("clientred.kzb"),
"kzb://clientred/Prefabs/ClientRedPrefab"
)
}
preloadClientPrefab 为给定的 clientId 创建 KanziViewAdapter,加载 kzb 文件,并实例化预设件。
无论是否预加载预设件,客户端应用程序的配置都相同。预加载仅影响客户端的启动时间。
备注
预加载的内容会保留在内存中,直到具有相同 clientId 的实际客户端连接并接管其所有权。如果始终没有客户端以给定的 clientId 连接,其预加载的资源将在服务的整个生命周期内保持分配状态。
设置跨应用程序迁移动画¶
借助跨应用程序迁移动画,您可以在 Kanzi 工程中的不同 Android 应用程序之间切换时创建无缝的视觉迁移。
要设置跨应用程序迁移:
将源应用程序和目标应用程序中的 Kanzi 服务视图配置为:
使用相同的 Kanzi Android Service 实例和相同的
clientId。在两个客户端应用程序中使用相同类型的 Kanzi 服务视图(例如
KanziServiceSurfaceView)。在 activity 中为全屏视图。
满足 Kanzi Android framework (droidfw) 的透明度要求。请参阅 创建透明视图:
在 Kanzi 服务视图中,设置:
将
transparency设置为true将
clearEnabled设置为true将
clearColor设置为@android:color/transparent
编辑
application.cfg以要求具有 alpha 位的表面。请参阅 颜色格式。SurfaceBitsAlpha = 8
在 Kanzi Studio 中选择显示透明内容的节点,并在 Properties 中将 Background Brush 属性设置为 < No Brush >。
使用以下参数调用
startSeamlessTransitionAPI:clientId- 在 Android 应用程序之间移动的客户端 ID。transitionDuration- Kanzi 内容在叠加层中保留的持续时间。kanziTransition- 更新 Kanzi 状态以驱动视觉过渡的 lambda 函数。androidTransition- 启动 Android 端应用程序过渡的 lambda 函数。leftMargin- 可选的左边距(以像素为单位),用于在叠加层中定位 Kanzi 内容。默认值:0。topMargin- 可选的上边距(以像素为单位),用于在叠加层中定位 Kanzi 内容。默认值:0。rightMargin- 可选的右边距(以像素为单位),用于在叠加层中定位 Kanzi 内容。默认值:0。bottomMargin- 可选的下边距(以像素为单位),用于在叠加层中定位 Kanzi 内容。默认值:0。
// Start a seamless transition between applications. kanziServiceView?.startSeamlessTransition( clientId = "coverflow", transitionDuration = 2.seconds, kanziTransition = { // Update Kanzi state to drive the visual transition. kanziServiceView?.pushDataInt( "kzb://service/Data Sources/Data source", "State", 1 ) }, androidTransition = { // Launch the target application. val intent = Intent().apply { setClassName( "com.example.clientred", "com.example.clientred.MainActivity" ) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } startActivity(intent) } )
调用此 API 时,Kanzi Android Service 会创建一个叠加窗口,在应用程序过渡期间显示动画。
为参与过渡的每个应用程序授予 Display over other applications 权限。
要授予此权限:
在设备上安装服务 host 应用程序和客户端应用程序。
对于参与过渡的每个应用程序:
打开设备设置。
导航至 Apps > <ApplicationName> > Permissions。
启用 Display over other applications 权限。
备注
在 Android Automotive 设备上,您无法通过 Android UI 为新安装的应用程序授予 Display over other applications 权限。
作为替代方案,您可以使用平台密钥对 APK 进行签名,以自动授予此权限。
已知问题¶
目前,即使没有任何客户端使用动画功能,服务 host 应用程序也需要 Display over other applications 权限。
另请参阅¶
使用 Kanzi Android framework (droidfw) 进行开发时
使用 Kanzi application framework (appfw) 开发 Android 应用程序。