2024年6月

MoneyPrinterPlus可以使用大模型自动生成短视频,我们可以借助Azure提供的语音服务来实现语音合成和语音识别的功能。

Azure的语音服务应该是我用过的效果最好的服务了,微软还得是微软。

很多小伙伴可能不知道应该如何配置,这里给大家提供一个详细的Azure语音服务的配置教程。

项目已开源,代码地址:
https://github.com/ddean2009/MoneyPrinterPlus

Azure的具体配置

因为Azure的注册需要用到VISA,所以阻止了很多想要进一步探索的小伙伴。

其实,MoneyPrinterPlus也是支持国内的云厂商,比如阿里云和腾讯云。所以,如果注册Azure有困难的小伙伴,可以参考我的另外两篇使用阿里云和腾讯云的介绍文章。

这里Azure为例,来讲解如何进行Azure语音的配置。

获取Azure的Speech Key和Service Region

首先我们到Azure的官网上去注册一个账号,怎么注册这里就不讲了,反正很简单,注册成功还可以免费使用一年的微软云服务,非常的棒。

有了账号,并且登录账号之后,在搜索框输入语音服务:

image-20240626152358026

点击下面的语音服务,进入到Azure的语音服务页面。

image-20240626152540065

到语音服务这里,点击创建按钮,会进入创建语音服务页面:

image-20240626152710532

填上必须的内容。点击审阅并创建,就可以创建好Azure的服务了。

记住你的密钥和region:

image-20240626152846707

在MoneyPrinterPlus中配置

启动我们的项目,在web页面点击最左边的基础配置,找到右边的配置音频库信息,选择Tencent。

填入我们之前保存的密钥和region。

回车后,这样我们的配置就保存了。

image-20240626152909378

其他的配置

资源库

资源库指的是我们从哪里获取视频或者图片信息,这里目前提供了两个资源提供方,分别是pexels和pixabay。

大家任意选择一个即可。

以pexels为例,我们登入pexels官网
https://www.pexels.com/zh-cn/
,注册一个账号。

在图片和视频API里面,可以查看自己的api密钥。

image-20240616211609578

查看自己的API密钥:

image-20240616211719443

把这个API密钥记下来,拷贝到MoneyPrinterPlus的配置即可。

![image-20240620170029227](/Users/wayne/Library/Application Support/typora-user-images/image-20240620170029227.png)

大模型配置

目前支持Moonshot,openAI,Azure openAI,Baidu Qianfan, Baichuan,Tongyi Qwen, DeepSeek这些。

国内要用的话推荐Moonshot(最近发现moonshot不太稳定,大家可以考虑Baichuan或者DeepSeek)。

同样的到Moonshot开发者平台上注册一个key:
https://platform.moonshot.cn/
填入对应的配置即可。

image-20240616212642905

AI短视频生成

有了基础配置之后,就可以点击左边的AI视频进入AI视频生成页面。

  1. LLM视频文案生成

在视频主题区输入你需要生成的视频主题,然后点击生成视频文案。

程序会自动使用大模型生成对应的视频文案和视频文案关键字:

image-20240616220713534

如果你对视频文案或者关键字不满意,可以手动进行修改。

  1. 视频配音区

在视频配音区可以选择配音语言和对应的配音语言,目前支持100+配音语言。

还可以选择不同的配音语速,以支持不同使用场景。

image-20240616220840076

如果你对配音不太确定,可以点击试听声音试听对应的配音语音。

  1. 背景音乐

背景音乐放在项目下的bgmusic目录下面,你可以自行添加背景音乐文件到该文件夹下面。

image-20240616221041774

可以选择是否开启背景音乐,和默认的背景音乐音量。

  1. 视频配置区

视频配置区可以选择视频布局:竖屏,横屏或者方形。

可以选择视频帧率,视频的尺寸。

还可以选择每个视频片段的最小长度和最大长度。

最最重要的,还可以开启视频转场特效。目前支持30+视频转场特效。

image-20240616221116997

  1. 字幕配置

如果你需要字幕,那么可以点击开启字幕选项,可以设置字幕字体,字幕字体的大小和字幕颜色等。

如果你不知道怎么设置,选择默认即可。

image-20240616221242812

  1. 最后的视频生成

最后点击生成视频按钮即可生成视频。

页面会有相应的进度提醒。

image-20240616221712173

最后生成的视频会展示在页面最下面,大家可以自行播放。

点我查看更多精彩内容:www.flydean.com

1、问题来源

最近的项目里面有个需求,要在打包之后的
exe
或者
apk
运行起来后访问工程
Content
或者安卓目录下的非
Asset
文件,比如
text
文件,
json
文件等,从中读取一些可随时修改的配置项信息。但是这些没法直接被
UE
平台序列化存储,因此需要做一点点额外的操作来实现我们的目标,假设工程目录
Content
里面有个
Json
目录,
Json
里面有个
A.json文件
,程序在运行时需要读取
A.json。

下面针对
Windows

Android
平台依次展开讨论怎么做。

2、Windows平台

第一步
,在
Project Settings->Project->Packaging下面,取消勾选Use Pak File,因为打包成.pak文件的话,Content目录下的所有资源(Asset文件或Non-Asset文件),都会被打包进一个.pak文件,后期无法随时修改。

第二步,在
Project Setting
顶部搜索
asset
,在
Additional Non-Asset Directories to Package右边点击加号,在新增的一行右侧点击"..."标记,新增一条路径指向Conent目录下的Json文件夹。

打包一下,文件夹里面显示我们的
Json/A
.json
已经作为独立文件成功打包到
WindowsNoEditor
目录下。

写几行测试一下看看行不行,假设我要蓝图里面读
A.json

C++
封装函数如下,其实主要是要通过
FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir()) + JsonName拼接得到A.json的正确绝对路径

1 bool UJsonOperator::FindValueWithGivenProperty(FString JsonName, FString FieldName, FString&Val)2 {3     FString AbsoluteJsonPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir()) +JsonName;4     if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*AbsoluteJsonPath)) {5         UE_LOG(MiniFileReaderLog, Warning, TEXT("%s"), *AbsoluteJsonPath); //do not call UKismetSystemLibrary::PrintString in static function
6 
7 FString JsonData;8         FFileHelper::LoadFileToString(JsonData, *AbsoluteJsonPath);9 
10         /*convert fstring to json object.*/
11         TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(JsonData);12         TSharedPtr<FJsonObject> RootJsonObj = MakeShareable(newFJsonObject);13 
14         if(FJsonSerializer::Deserialize(JsonReader, RootJsonObj)) {15             UE_LOG(MiniFileReaderLog, Warning, TEXT("Json Data: %s"), *JsonData);16 
17             TSharedPtr<FJsonValue> Member = RootJsonObj->TryGetField(FieldName);18             if(Member) {19                 Val = Member->AsString();20                 UE_LOG(MiniFileReaderLog, Log, TEXT("Property [%s], Value: %s"), *FieldName, *(Member->AsString()));21 }22             else{23                 UE_LOG(MiniFileReaderLog, Warning, TEXT("Property [%s] specified not exists."), *FieldName);24                 return false;25 }26 }27         else{28             UE_LOG(MiniFileReaderLog, Warning, TEXT("FJsonSerializer::Deserialize Failed!"));29             return false;30 }31 }32     else{33         UE_LOG(MiniFileReaderLog, Error, TEXT("File : %s not exists."), *JsonName);34         return false;35 }36     return true;37 }

3、Android平台

Android
平台略有不同,

UE
工程发布到安卓设备并启动之后,会在安卓设备的
/storage/emulated/0/UE4Game
目录下新建一个和
UE Project Name
的同名目录。该目录存放程序的运行数据,此处工程名假设为
FunctionProject

第一步,在运行数据目录新建
Json/A.json
,如下所示,里面存放程序需要读取的属性数据信息。

第二步,写一段示例代码读取到该目录下的A.json文件,如下所示:

bool UJsonOperator::LoadRawContentsToString(FString FileName, FString&FileContents)
{
#if PLATFORM_ANDROID externFString GFilePathBase;
FString tmp
= GFilePathBase + FString("/UE4Game/") + UKismetSystemLibrary::GetGameName() + FString("/") +FileName;
GEngine
->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, FString::Printf(TEXT("%s"), *tmp));return FFileHelper::LoadFileToString(FileContents, *tmp);#else const FString ThePath =FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir());
UE_LOG(MiniFileReaderLog, Log, TEXT(
"Json Path: %s"), *(ThePath +FileName));return FFileHelper::LoadFileToString(FileContents, *(ThePath +FileName));#endif}

此处,
UKismetSystemLibrary::GetGameName()
就是获取
UE4Game
目录下的
APP
发布名称,可以理解为
Android APP
运行数据的根目录,函数传入参数的第
1
个参数
FileName

Json/A.json
,那么最终拼接得到的
A.json
绝对路径为:

/storage/emulated/0/UE4Game/FunctionProject/Json/A.json

拿到这个绝对路径,就可以读取文件内容了,到此结束!

随着模型规模的增长,生成式人工智能的实现需要大量的推理资源。这不仅增加了每次生成的成本,而且还增加了用于满足此类请求的功耗。因此,文本生成的推理优化对于降低延迟、基础设施成本以及功耗都至关重要,其可以改善用户体验并提高文本生成任务的效率。

辅助解码是一种用于加速文本生成的流行方法。我们在英特尔 Gaudi2 上对其进行了适配和优化,使得其性能与英伟达 H100 GPU 相当,一如我们在
之前的博文
中所展示的,但 Gaudi2 的价格仅相当于英伟达 A100 80GB GPU。这项工作现已集成入 Optimum Habana,Optimum Habana 对 Transformers 和 Diffusers 等各种 Hugging Face 库进行了扩展,以在英特尔 Gaudi 处理器上对用户的工作流进行全面优化。

投机采样 - 辅助解码

投机采样是一种用于加速文本生成的技术。其工作原理是用一个草稿模型一次生成 K 个词元,再由目标模型对这 K 个生成词元进行评估。如若草稿模型生成的某个位置的词元被拒绝,则用目标模型来生成该位置的词元,并丢弃草稿模型生成的随后词元,反复执行上述过程直至结束。使用投机采样,可以提高文本生成的速度并得到与原始自回归采样相当的生成质量。使用该技术时,用户可以指定草稿模型。数据证明,推测采样可为基于 transformer 的大模型带来约 2 倍的加速。一句话概括,投机采样可以加速文本生成并提高英特尔 Gaudi 处理器上的文本生成性能。

然而,草稿模型和目标模型 KV 缓存尺寸不同,因此同时分别对这两个模型进行优化显得尤为重要。本文,我们假设目标模型为一个量化模型,并利用 KV 缓存和投机采样对其进行加速。请注意,这里每个模型都有自己的 KV 缓存。我们用草稿模型生成 K 个词元,然后用目标模型对其进行评估; 当草稿模型生成的词元被拒绝时,目标模型会用于生成被拒绝位置的词元,并丢弃草稿模型生成的随后词元; 接着草稿模型继续生成接下来的 K 个词元,如此往复。

请注意,文献 [2] 证明了执行投机采样可以恢复目标模型的分布 - 这从理论上保证了投机采样可以达到与对目标模型自身进行自回归采样相同的采样质量。因此,不采用投机采样的理由仅在于收益,如草稿模型的尺寸并没有足够的比较优势,抑或是草稿模型生成词元的接受比太低。

辅助生成是一种类似于投机采样的技术,其大约与投机采样同一时间被独立发明出来 [3]。其作者将此方法集成到了 Hugging Face Transformers 中,现在模型的
.generate()
的方法中有一个可选的
assistant_model
参数用于启用辅助生成。

用法及实验

在 Gaudi 上使用辅助生成非常简单,我们在
这里
提供了一个示例。

顾名思义,参数
--assistant_model
用于指定草稿模型。草稿模型用于生成 K 个词元,然后由目标模型对其进行评估。当草稿模型生成的词元被拒绝时,目标模型会自己生成该位置的词元,并将草稿模型生成的该位置之后的词元丢弃。接着,草稿模型再生成接下来的 K 个词元,如此往复。草稿模型的接受率部分取决于模型选择,部分取决于输入文本。一般情况下,辅助生成能将大型 transformer 族模型的速度提高约 2 倍。

总结

Gaudi 现已支持用户简单易用地使用辅助生成加速文本生成,用户可用其进一步提高英特尔 Gaudi 处理器的性能。该方法基于投机采样,已被证明可以有效提高基于大型 transformer 模型的性能。

参考文献

[1] N. Shazeer,Fast Transformer Decoding: One Write-Head is All You Need,Nov. 2019,arXiv:1911.02150.

[2] C. Chen,S. Borgeaud,G. Irving,J.B. Lespiau,L. Sifre,J. Jumper, Accelerating Large Language Model Decoding with Speculative Sampling,Feb. 2023,arXiv:2302.01318

[3] J. Gante,辅助生成: 低延迟文本生成的新方向,May 2023,
https://hf.co/blog/zh/assisted-generation


英文原文:
https://hf.co/blog/assisted-generation-support-gaudi

原文作者: Haim Barad,Tien Pei Chou

译者: Matrix Yao (姚伟峰),英特尔深度学习工程师,工作方向为 transformer-family 模型在各模态数据上的应用及大规模模型的训练推理。

前言

毫无疑问,组件通信是Vue中非常重要的技术之一,它的出现能够使我们非常方便的在不同组件之间进行数据的传递,以达到数据交互的效果。所以,学习组件通信技术是非常有必要的,本文将总结Vue中关于组件通信的八种方式,帮助大家在使用Vue的过程中更加得心应手!

如果文中有不对、疑惑的地方,欢迎在评论区留言指正!!

一、什么是组件通信

在开始之前我们需要明白什么是组件通信,
组件通信
可以拆分为两个部分:

  • 组件
  • 通信

都知道组件是
vue
最强大的功能之一,
vue
中每一个
.vue
文件我们都可以视之为一个组件,简单来说组件就是对UI结构的复用。

通信指的是发送者通过某种媒体以某种格式来传递信息到收信者以达到某个目的。广义上,任何信息的交通都是通信。而
组件间通信
即指组件(
.vue
)通过某种方式来传递信息以达到某个目的,举个栗子我们在使用
UI
框架中的
table
组件,可能会往
table
组件中传入某些数据,这个本质就形成了组件之间的通信

二、为什么要进行组件通信

通信的本质是信息同步,共享。回到vue中,每个组件之间的都有独自的作用域,组件间的数据是无法共享的但实际开发工作中我们常常需要让组件之间共享数据,这也是组件通信的目的要让它们互相之间能进行通讯,这样才能实现数据间的交互,完成某种功能的开发。

三、组件通信的分类

组件间通信的分类可以分成以下

  • 父子组件之间的通信
  • 兄弟组件之间的通信
  • 祖孙与后代组件之间的通信
  • 非关系组件间之间的通信

他们之间的关系如下图:

组件

目前最常用是
props/$emit

vuex/pinia
,接下来是
provide/inject
,其他不建议使用;
实际项目中,简单父子组件传递采用
props/$emit
,涉及全局共享的数据一般采用
vuex/pinia
结合存储对象
localStorage/sessionStorage
使用。

Vue3 组件通信方式

四、Vue3 的八种组件通信方式

  • props
  • $emit
  • expose / ref
  • $attrs
  • v-model
  • provide / inject
  • Vuex
  • mitt

五、Vue3 八种通信方式用法讲解

1. props

用 props 传数据给子组件有两种方法,如下

方法一,setup() 方法写法

// Parent.vue 传送
<child :msg1="msg1" :msg2="msg2"></child>
<script>
import child from "./child.vue"
import { ref, reactive } from "vue"
export default {
    data(){
        return {
            msg1:"这是传级子组件的信息1"
        }
    },
    setup(){
        // 创建一个响应式数据
        
        // 写法一 适用于基础类型  ref 还有其他用处,下面章节有介绍
        const msg2 = ref("这是传级子组件的信息2")
        
        // 写法二 适用于复杂类型,如数组、对象
        const msg2 = reactive(["这是传级子组件的信息2"])
        
        return {
            msg2
        }
    }
}
</script>

// Child.vue 接收
<script>
export default {
  props: ["msg1", "msg2"],// 如果这行不写,下面就接收不到
  setup(props) {
    console.log(props) // { msg1:"这是传给子组件的信息1", msg2:"这是传给子组件的信息2" }
  },
}
</script>

方法二,setup 语法糖

// Parent.vue 传送
<child :msg2="msg2"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg2 = ref("这是传给子组件的信息2")
    // 或者复杂类型
    const msg2 = reactive(["这是传级子组件的信息2"])
</script>

// Child.vue 接收
<script setup>
    // 不需要引入 直接使用
    // import { defineProps } from "vue"
    const props = defineProps({
        // 写法一
        msg2: String
        // 写法二
        msg2:{
            type:String,
            default:""
        }
    })
    console.log(props) // { msg2:"这是传级子组件的信息2" }
</script>

注意:

如果父组件是setup(),子组件setup 语法糖写法的话,是接收不到父组件里 data 的属性,只能接收到父组件里 setup 函数里传的属性。

如果父组件是setup 语法糖写法,子组件setup()方法写法,可以通过 props 接收到 data 和 setup 函数里的属性,但是子组件要是在 setup 里接收,同样只能接收到父组件中 setup 函数里的属性,接收不到 data 里的属性

官方也说了,既然用了 3,就不要写 2 了,所以不推荐setup()方法写法。下面的例子,一律只用语法糖的写法。

2. $emit

// Child.vue 派发
<template>
    // 写法一
    <button @click="emit('myClick')">按钮</buttom>
    // 写法二
    <button @click="handleClick">按钮</buttom>
</template>
<script setup>
    
    // 方法一 适用于Vue3.2版本 不需要引入
    // import { defineEmits } from "vue"
    // 对应写法一
    const emit = defineEmits(["myClick","myClick2"])
    // 对应写法二
    const handleClick = ()=>{
        emit("myClick", "这是发送给父组件的信息")
    }
    
    // 方法二 不适用于 Vue3.2版本,该版本 useContext()已废弃
    import { useContext } from "vue"
    const { emit } = useContext()
    const handleClick = ()=>{
        emit("myClick", "这是发送给父组件的信息")
    }
</script>

// Parent.vue 响应
<template>
    <child @myClick="onMyClick"></child>
</template>
<script setup>
    import child from "./child.vue"
    const onMyClick = (msg) => {
        console.log(msg) // 这是父组件收到的信息
    }
</script>

3. expose / ref

父组件获取子组件的属性或者调用子组件方法。

// Child.vue
<script setup>
    // 方法一 不适用于Vue3.2版本,该版本 useContext()已废弃
    import { useContext } from "vue"
    const ctx = useContext()
    // 对外暴露属性方法等都可以
    ctx.expose({
        childName: "这是子组件的属性",
        someMethod(){
            console.log("这是子组件的方法")
        }
    })
    
    // 方法二 适用于Vue3.2版本, 不需要引入
    // import { defineExpose } from "vue"
    defineExpose({
        childName: "这是子组件的属性",
        someMethod(){
            console.log("这是子组件的方法")
        }
    })
</script>

// Parent.vue  注意 ref="comp"
<template>
    <child ref="comp"></child>
    <button @click="handlerClick">按钮</button>
</template>
<script setup>
    import child from "./child.vue"
    import { ref } from "vue"
    const comp = ref(null)
    const handlerClick = () => {
        console.log(comp.value.childName) // 获取子组件对外暴露的属性
        comp.value.someMethod() // 调用子组件对外暴露的方法
    }
</script>

4. attrs

attrs
:包含父作用域里除 class 和 style 除外的非 props
属性集合

// Parent.vue 传送
<child :msg1="msg1" :msg2="msg2" title="3333"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg1 = ref("1111")
    const msg2 = ref("2222")
</script>

// Child.vue 接收
<script setup>
    import { defineProps, useContext, useAttrs } from "vue"
    // 3.2版本不需要引入 defineProps,直接用
    const props = defineProps({
        msg1: String
    })
    // 方法一 不适用于 Vue3.2版本,该版本 useContext()已废弃
    const ctx = useContext()
    // 如果没有用 props 接收 msg1 的话就是 { msg1: "1111", msg2:"2222", title: "3333" }
    console.log(ctx.attrs) // { msg2:"2222", title: "3333" }
    
    // 方法二 适用于 Vue3.2版本
    const attrs = useAttrs()
    console.log(attrs) // { msg2:"2222", title: "3333" }
</script>

5. v-model

可以支持多个数据双向绑定

// Parent.vue
<child v-model:key="key" v-model:value="value"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const key = ref("1111")
    const value = ref("2222")
</script>

// Child.vue
<template>
    <button @click="handlerClick">按钮</button>
</template>
<script setup>
    
    // 方法一  不适用于 Vue3.2版本,该版本 useContext()已废弃
    import { useContext } from "vue"
    const { emit } = useContext()
    
    // 方法二 适用于 Vue3.2版本,不需要引入
    // import { defineEmits } from "vue"
    const emit = defineEmits(["key","value"])
    
    // 用法
    const handlerClick = () => {
        emit("update:key", "新的key")
        emit("update:value", "新的value")
    }
</script>

6. provide / inject

provide / inject 为依赖注入

provide
:可以让我们指定想要提供给后代组件的数据或

inject
:在任何后代组件中接收想要添加在这个组件上的数据,不管组件嵌套多深都可以直接拿来用

// Parent.vue
<script setup>
    import { provide } from "vue"
    provide("name", "RDIF")
</script>

// Child.vue
<script setup>
    import { inject } from "vue"
    const name = inject("name")
    console.log(name) // RDIF
</script>

7. Vuex

// store/index.js
import { createStore } from "vuex"
export default createStore({
    state:{ count: 1 },
    getters:{
        getCount: state => state.count
    },
    mutations:{
        add(state){
            state.count++
        }
    }
})

// main.js
import { createApp } from "vue"
import App from "./App.vue"
import store from "./store"
createApp(App).use(store).mount("#app")

// Page.vue
// 方法一 直接使用
<template>
    <div>{{ $store.state.count }}</div>
    <button @click="$store.commit('add')">按钮</button>
</template>

// 方法二 获取
<script setup>
    import { useStore, computed } from "vuex"
    const store = useStore()
    console.log(store.state.count) // 1

    const count = computed(()=>store.state.count) // 响应式,会随着vuex数据改变而改变
    console.log(count) // 1 
</script>

8. mitt

Vue3 中没有了 EventBus 跨组件通信,但是现在有了一个替代的方案 mitt.js,原理还是 EventBus。

先安装
npm i mitt -S

然后像以前封装 bus 一样,封装一下

mitt.js
import mitt from 'mitt'
const mitt = mitt()
export default mitt

然后两个组件之间通信的使用

// 组件 A
<script setup>
import mitt from './mitt'
const handleClick = () => {
    mitt.emit('handleChange')
}
</script>

// 组件 B 
<script setup>
import mitt from './mitt'
import { onUnmounted } from 'vue'
const someMethed = () => { ... }
mitt.on('handleChange',someMethed)
onUnmounted(()=>{
    mitt.off('handleChange',someMethed)
})
</script>

六、参考资料

vue.js:
https://cn.vuejs.org/

vuex是什么:
https://vuex.vuejs.org/zh/

工作中要使用Git,看这篇文章就够了:
http://www.guosisoft.com/article/detail/410508049313861

企业数字化转型如何做?看过来:
http://www.guosisoft.com/article/detail/408745545576517

Vue2.x 组件通信方式:
http://www.guosisoft.com/article/detail/411234710110277

【保姆级教程】Vue项目调试技巧:
http://www.guosisoft.com/article/detail/430312211521605

Vue 前端开发团队风格指南(史上最全):
http://www.guosisoft.com/article/detail/415491255230533

国思RDIF低代码快速开发平台(支持vue2、vue3):
http://www.guosisoft.com/article/detail/557095625134149

七、结语

如果本文对你有一点点帮助,点个赞支持一下吧,你的每一个【赞】都是我创作的最大动力
_

更多技术文章请往:

http://www.guosisoft.com/article

http://www.rdiframework.net/article

大家一起共同交流和进步呀!!

移动互联网时代,各种以用户为中心的App如春笋般涌现,满足了用户在购物、导航、娱乐等不同场景下的需求,不同程度上丰富了用户的日常生活。然而,随着App种类的激增,用户在享受多样化服务的同时,也面临着多任务管理的挑战。

比如,用户正沉浸在游戏或追剧中时,能否想起并打开App查看一下:不久前点的外卖是否已上路?网约车是否已到达?餐厅预订的餐品是否已准备好?在多应用进程并行使用的情况下,这些重要的服务很容易被忽略,从而影响到用户的日常生活质量与效率。

那么如何才能将诸如外卖、打车、取餐等长时间进程的服务通过显性化的形式,在不影响设备现有操作的情况下,实现服务进展的有效提示,帮助业务实现服务的高效闭环呢?

HarmonyOS SDK实况窗服务(Live View Kit)
支持应用将订单或服务的实时状态信息变化在设备的熄屏、锁屏、通知中心、状态栏等关键界面展示,并对展示信息的生命周期、用户界面UI效果等进行管理,帮助用户聚焦正在进行的任务,方便查看和即时处理通知内容。

image

其实,实况窗对于广大用户来说并不陌生,最基础的应用场景就是手机来电、文件传输、音频录音等常见功能的提示,这些功能场景的共性在于,能够在限定的时间段内及时提供有价值且需用户密切关注的信息,确保用户能够迅速地进行查看和操作。

image

同理,实况窗场景也可以适用于出行打车、高铁/火车、排队等场景,具体可支持对接的场景如下表所示:

image

在设计特定应用场景的实况窗时,开发者需要考虑应用服务进程中需要设置提醒的关键节点与呈现的内容信息,这也是用户在使用实况窗过程中最关注的部分。我们以即时配送的外卖配送场景来举例说明具体一下整个服务流程中各个节点的主要展示内容。外卖配送的关键节点可分为用户下单、等待用户支付、等待商家接单、商家已接单、骑手接单、骑手已到店、骑手配送中、商品已送,用户可以通过实况窗实时得知外卖的配送进度,而无需频繁点开应用详情页查看。

image

当然,实况窗中所展示的业务服务进程并不一定是如预期般顺利的,它还存在一定的变化性,比如,当航班登机口发生变更,在打车时司机突然取消订单,或者外卖配送订单超时,这些实时信息都需要在实况窗中及时告知用户,保证用户在实况窗中得到的信息都是最新的状态。

此外,实况窗还具有一定的时效性,在发生的特定时间段内,向用户提供实时刷新的价值信息,该时间段有明确的界定,单个实况窗的生命周期最长不超过8小时,并且需要及时更新实况窗消息,系统将在实况窗超过2小时未更新时,隐藏实况窗在状态栏胶囊和锁屏的展示,保留通知中心展示,若超过4小时未更新,系统会认为实况窗结束,并从各个展示入口清除该实况窗。而在航班、高铁火车等服务进程较长的场景中,如果预定的是2天后的机票/车票,那么在刚买时不作提醒,而在出发前开始提示,具体提醒时间根据业务实际情况确定。

实况窗还具有极强的互动性。它会在设备状态栏、应用界面、通知中心、锁屏等多种界面将信息即时触达给用户,并且当实况窗当展示在通知中心和锁屏位置时,顶部会默认显示全量实况窗。当展示的内容是来电、录音等需要操作的场景时,实况窗还会提供快捷操作按钮,满足便捷操作的诉求。

image

实况窗有胶囊态和卡片态两种呈现形态,但同一个事件活动在不同场景下仅出现一种形态,如果当前事件所承载的落地页在前台,则没有胶囊态,如果当前设备为通知中心/锁屏状态,那么就只显示实况窗的卡片态,不显示状态栏胶囊。当显示为胶囊态时,实况窗也可支持点击交互,用户可点击单个胶囊,呼出悬浮卡片,胶囊随之消失,然后点击卡片空白处,即可进入对应的详情页进行具体操作。

image

作为HarmonyOS的新型消息通知展示形式,实况窗服务是一个能够适应不同业务场景并提供实时信息更新的应用功能,它具有变化性、时效性、互动性等特征优势。对于广大应用开发者来说,合理使用实况窗可有效提升用户服务体验,确保用户能够及时获取和响应关键信息,实现服务的快速闭环,大大提高了业务履约效率。

了解更多详情>>

获取实况窗服务开发指导文档