bilibili PC客户端架构设计——基于Electron

news/2024/5/3 1:40:27

众所周知,bilibili是个学习的网站,网页端和粉版移动端都非常的好用,不过,相对其它平台来说bilibili的PC客户端也算是大器晚成了。在有些场景PC客户端的优势也是显而易见的,比如,跓留电脑桌面的快捷、独立的应用窗口、特有的交互方式等,因此就有了很多喜欢bilibili的技术大佬整出了不少体验不错的第三方PC应用,比如云之幻版、逍遥橙子版等。

当然bilibili也有一个源自三方的UWP客户端,但是由于历史原因一直没有得到很好的维护,在21年底,一个需要在电脑端预装的需求让我们有了开发一个全新的PC客户端的想法。

图片

一、PC客户端的技术选型

选择一个什么样的技术,可以在降本增效的大背景下实现一个体验相对较好、易维护、跨平台且能快速推进的PC应用呢?

图片

图2 PC端开发技术对比

调研了一些PC端应用开发的技术方案之后,基于开发效率、跨平台和成熟度等因素考虑,最终bilibili选择了使用WEB技术栈的Electron做为新PC客户端的开发框架。

图片

图3 Electron框架的优势

在确定了开发框架后,先来看看Electron到底是什么。我们知道Electron是背靠微软基于WEB技术的开源项目,最初源自GitHub团队的Atom Shell项目,结合NodeJs和Chromium实现,并对系统API做了封装,比如系统对话框、系统托盘、系统菜单、剪切板等,开发者可以直接通过js访问这些API。

图片

图4 Electron的构成

图片

图5 一般Electron应用的运行时

我们知道Electron程序主要分为主进程(Main Process)和渲染进程(Render Process),主进程主要为NodeJs和原生API,渲染进程主要为前端技术,主进程和渲染进程之间通过IPC进行通信。

为了易维护、好拓展、可复用等目的,我们选择了NodeJs+NestJs+Esbuild的方案来开发主进程,选择了和WEB首页Laputa项目相同的技术栈Vue3+Vite2+TypeScript开发渲染进程。

图片

图6 PC客户端技术选型

主进程的NestJs是一个受Angular启发的NodeJS后端框架,风格有点像SpringBoot,结合OOP (Object Oriented Programming)、FP (Functional Programming)和 FRP (Functional Reactive Programming)等编程范式,能够开发出高可测、好拓展、松耦合、易维护的应用。Esbuild是使用go语言实现的js极速打包工具,可以助力更高效的开发主进程。

图片

图7 Esbuild官网展示的构建性能对比

渲染进程使用和WEB相同的技术栈,除了方便复用WEB现成的很多组件之外,同时WEB业务线的开发人力也可以无障碍的投入到PC客户端的业务开发中来,因此可以最大限度的降低开发成本、提高开发效率。另外,我们还在项目中实践和应用了我们自研的Vue3组件库vivid-ui、函数库@bilibili/b-utils和样式库@bilibili/b-style等工具库。

图片

图8 PC客户端自研工具库

二、PC客户端的整体架构

PC客户端是bilibili的一个全新的端,除了推荐、直播、热门、OGV、动态、空间、搜索等常规功能之外,还需具备电脑端本地软件的其它能力,包括安装升级、托盘隐藏、本地存储、专有窗口、离线缓存、自定义配置、唤端拉活等等。

图片

图9 下载中心

(其中Windows版、MacOS版、微软商店版都是PC客户端)

我们先来看看PC客户端的业务功能设计,在1.0的版本我们其实就已经实现了安装卸载、开屏页、首页、动态、搜索、我的和空间、播放页、登录、主题和设置等功能,随着版本迭代,我们陆续完成了一键三连安装界面、离线缓存、播单、消息等功能。

图片

图10 PC客户端业务功能设计

在明确业务功能后,结合框架和技术栈,我们设计了PC客户端的整体架构:由渲染进程实现业务功能的UI和交互,由主进程实现本地日志、存储、下载、上报、升级等服务和支持的功能,渲染进程和主进程之间封装IPC为统一的JSB接口进行通信。

图片

图11 PC客户端整体架构设计

在渲染进程中,账号登录、大会员充值、直播间和开播页都是使用webview嵌WEB页的方式接入的,其它业务模块均为本地应用页面;在主进程中,本地日志、本地存储和下载SDK都是引入的模块,其它各个模块和服务之间通过依赖注入和rxjs主题订阅的方式实现相互调用;JSB则由主进程通过preload的方式注入到渲染进程各页面的window对象中,渲染进程不论是外嵌还是本地页面都能访问到JSB对象实现和主进程通信。

由于客户端基于Electron和WEB技术开发,一个窗口页面其实本质上就是一个HTML页面。当然,我们的主页面都是从由主进程创建的本地服务器上拉取的Local页面,因此断网的情况也能响应,像首页、动态网络强相关的页面,在断网时会显示失败提示,而离线缓存、设置等功能都能正常使用。

由于我们渲染层是基于Vue开发的SPA应用,主窗口和播放窗口在打开时会有一个比较耗时的加载过程,为了提升用户体验我们专门针对窗口创建和打开过程做了优化。

  1. 在启动时(首次打开主窗口),会先创建一个隐藏的主窗口,加载一个相对简单的开屏页。

  2. 当开屏页渲染完成后,调用主进程splashPageReady接口,主进程控制显示主窗口。(防止开屏页白屏)

  3. 主窗口和开屏页显示之后,在主窗口开屏页下方加载和渲染比较大的主页面。

  4. 主页面渲染完成时,调用主进程mainWindowReady接口,并关闭盖在主页面上面的开屏页。

  5. 主进程收到mainWindowReady后,创建一个隐藏的播放窗口。

  6. 当用户点击播放视频时,显示已渲染好的播放窗口,并加载和播放视频。

  7. 关闭主窗口和播放窗口时,只将窗口隐藏并不真实销毁窗口实例,当再次打开或播放视频时,就可以快速打开窗口,提升用户体验。

图片

图12 PC客户端执行过程

图片

图13 窗口创建和打开流程

事实上,采用隐藏的窗口进程做预渲染和并行处理是提升Electron应用体验的常用方法,在Electron中可以用ChildProcess、BrowserWindow、BrowserView、webview等方式开启子进程,突破进程限制,充分利用机器的性能。

可以通过主进程app.getAppMetrics()方法获取APP各进程的CPU和内存数据统计,PC客户端主要包含7个常跓进程:1个浏览器(Browser)进程、1个GPU进程、1个网络服务(Network Service)进程、1个音频服务(Audio Service)进程和3个页面(Tab)进程。

图片

图14 PC客户端的进程

由于渲染进程占用资源比较大,如果用户长时间未使用又没有关闭的话就会造成资源浪费,因此我们还为APP设计了“节能模式”,当APP隐藏到托盘超过一定时间未点开后会进入到此模式,这时程序会杀掉主窗口和播放窗口对应的渲染进程,释放资源占用。进入节能模式之后,如果用户再打开APP则会进入前面提到的窗口创建和打开流程,不过这个过程没有APP初始化的任务,速度会比冷启动时要快很多。

图片

图15 PC客户端节能模式

PC客户端使用Fawkes平台打包构建、进行数据上报并建立数据报表、平台进行自动化巡检测试、建有专门的产品和开发文档,开发方面也有明确的代码规范要求

图片

图16 项目设计开发平台工具

三、PC客户端工程化构建

PC客户端渲染进程和主进程都是基于TypeScript进行开发的,Ts提供的类型定义和规范可以大幅度提高代码的可读性,在主进程和渲染进程的中间,我们还设计了一个common共享层,用来实现Ts类型和常量的共享。

PC客户端项目结构:

bilibili-electron├── app/*                              # 构建生成代码├── build/*                            # 构建资源目录├── dist/*                             # 安装包生成目录├── scripts/*    ├── build.main.ts                  # 主进程构建配置├── src    ├── common/*                       # 共享类型    ├── main/*                         # 主进程    └── render/*                       # 渲染进程├──  electron-builder.config.js        # 安装包打包配置├──  vite.config.ts                    # 渲染进程构建配置├──  pckage.json

PC客户端项目运行和构建都基于NPM Script,构建release包只需要运行: yarn build:prod。构建代码位于app目录下,由于构建产物也是js代码,很容易被人拿去套壳、植入或篡改。为了防范这些所知的风险,增加被篡改和攻击的难度,我们需要对构建包进行加密处理。经过不断的迭代优化,我们实现了一套使用结合代码混淆、对称加密、字节码处理、WASM解密啥希校验的组合式客户端加密防破解方案。

在打Release包时,会把构建生成的代码先进行混淆、压缩处理,再将主进程主程序代码使用AES-256对称加密生成/app/main/.biliapp文件,将解密和执行入口使用字节码处理,最后计算出APP的哈希值。

图片

图17 PC客户端主程序加密

当PC客户端启动时,会先根据操作系统和系统架构找到对应字节码入口、解密并运行主程序、主程序中校验APP的哈希值,在确定运行环境安全后再正常启动程序。

图片

图18 pc客户端运行流程

PC客户端基于electron-builder进行打包,Mac的安装窗口通过electron-builder配置可以直接生成,Windows的专属一键三连安装程序则是使用NSIS + QT独立开发的。

图片

图19 PC客户端安装引导界面

图片

图20 基于NSIS+QT的专属一键三连界面

PC客户端前期版本基于electron-updater进行升级更新,每次进行版本升级后,都需要用户重新下载完整的安装包进行覆盖安装,由于安装包体积比较大在网络比较差的情况下,升级过程会比较耗时影响用户体验。

在1.9.x版本之后,我们已集成增量更新的功能,只需对app.asar代码包进行更新而不是包含框架的完整安装包,因此可以提高了升级更新效率和体验。

图片

图21 PC客户端升级更新流程

四、PC客户端的播放体验

类似爱奇艺和腾讯视频,我们的PC客户端也是双窗口模式,即主窗口和播放窗口是独立的,可以让用户一边播放视频一边刷动态、逛空间、看消息。

得益于PC客户端渲染进程使用WEB技术开发,稍加适配,PC客户端的播放器直接用上了WEB强大的Nano播放器,这使得PC客户端也拥有了高级弹幕、播放设置、播放器快捷键等功能。PC客户端还将UGC和OGV播放器集成在同一个播放窗口,让视频类型切换更加顺畅,还可以通过前进后退播放历史纪录。

PC客户端的播放质量对比WEB播放页来说,首帧和VV卡顿率会优于WEB,主要原因为能更多预载,单实例,编码支持统一;而百分钟卡顿率和错误率相对WEB来说要略高一些,新平台有更多case需要处理,此项会略高但重试机制尽量不影响体验。

图片

图22 PC客户端和WEB播放页质量对比

对比bilibili其它端来说,PC客户端的人均播放时长是最高的,目前均值已超80分钟,峰值可达100分钟。

另外,PC客户端还实现了贴心的“小窗”播放模式,当点击播放窗口右上角的小窗模式按钮时或拖动改变播放窗口到小尺寸时便会进入小窗模式。

在小窗模式下:

  • 播放窗口可以适配视频大小比例,实现无黑边播放。

  • 搭配固定窗口在最前端的功能,可以让用户一边看教程学习,一边实际动手操作。

  • 相对网页的画中画,PC客户端的小窗不仅可以显示弹幕,而且还没有原画页面的各种限制。

  • 这里小窗其实指的是一种简洁播放模式,因为小窗也可以拖动边缘调整大小,变成比“大窗”还大。

图片

图23 极致的小窗播放体验

图片

图24 固定小窗在最前端

五、PC客户端的质量保证

PC客户端在每次上线之前都需要经过严格的测试流程,一般在官网上线前4个工作日需要进行集成测试,前2个工作日出总包并进行总包回归测试。

除了功能测试之外,构建也需要保证准确性,PC客户端使用Fawkes打包构建,但是人工构建每次都需要根据不同的渠道选择不同的环境变量,渠道也比较多构建容易出错,因此我们开发了CLI打包工具。

命令行打包工具打通了Fawkes构建平台和魔镜测试平台,通过命令行选择渠道、类型和其它参数,并使用配置列表发起构建。

  1. 快速发起Fawkes构建,提高打包配置效率。

  2. 多种模式可选,快速发起debug/release、签名、推CD的包。

  3. 可以避免渠道包打了debug或未签名的情况。

  4. 可以避免环境变量配错、渠道号和渠道名对不上的问题。

  5. 接入魔镜自动化测试平台,保证构建包可靠性,不用人力挨个校验,提高构建效率。

图片

图25 通过命令行工具发起构建

图片

图26 魔镜PC客户端自动化测试

六、PC客户端发布和上线

PC客户端于22年5月10日在官网正式上线,在bilibili WEB首页顶导、bilibili下载中心和常用的PC的软件平台如:微软商店、联想商店、华为应用市场、腾讯管家等搜索“哔哩哔哩”都能下载到最新版本。

PC客户端的发布渠道较多,不同的渠道也有不同的要求,新版本会根据渠道ID来构建并分别投放上架。

图片

图27 PC客户端渠道汇总

上线以来,PC客户端DAU稳步增长,目前已达300万+。

图片

图28 PC客户端DAU数据稳步增长

各渠道安装量稳步上长,对比官网包占比已达40%。

图片

图29 微软商店~入选精选娱乐应用

图片

图30 华为应用市场~闲暇时光首选

图片

图31 联想应用商店~搜索“哔哩哔哩”


http://www.mrgr.cn/p/60064438

相关文章

如何将 ASP.NET Core MVC 项目的视图分离到另一个项目

如何将 ASP.NET Core MVC 项目的视图分离到另一个项目 在当下这个年代 SPA 已是主流,人们早已忘记了 MVC 以及 Razor 的故事。但是在某些场景下 SSR 还是有意想不到效果。比如某些静态页面,比如追求首屏加载速度的时候。最近在项目中回归传统效果还是不错。 有的时候我们希望…

Scala 03 —— Scala Puzzle 拓展

Scala 03 —— Scala Puzzle 拓展 文章目录 Scala 03 —— Scala Puzzle 拓展一、占位符二、模式匹配的变量和常量模式三、继承 成员声明的位置结果初始化顺序分析BMember 类BConstructor 类 四、缺省初始值与重载五、Scala的集合操作和集合类型保持一致性第一部分代码解释第二…

2001-2022上市公司数字化转型数据(含原始数据+计算代码+计算结果)

2001-2022上市公司数字化转型数据(含原始数据计算代码计算结果) 1、时间:2001-2022年 2、来源:原始数据整理自wind 3、指标:证券代码、证券简称、统计截止日期、是否发生ST或*ST或PT、是否发生暂停上市、行业代码、…

aardio 两行代码 调用 libxl 读写 excel

废话不多说, 直接给连接 https://files.cnblogs.com/files/blogs/762462/libxl.7z?t=1713539927&download=true 运行效果:文件存放路径:再上代码 import dotNet import consolexl = dotNet.load("libxl.net", "libxl.net.dll"); xl.import("libx…

【进阶六】Python实现SDVRPTW常见求解算法——离散粒子群算法(DPSO)

基于python语言,采用经典离散粒子群算法(DPSO)对 带硬时间窗的需求拆分车辆路径规划问题(SDVRPTW) 进行求解。 目录 往期优质资源1. 适用场景2. 代码调整2.1 需求拆分2.2 需求拆分后的服务时长取值问题 3. 求解结果4. …

python 爬虫

python 爬虫 1.开发工具 pycharm: https://pan.baidu.com/s/1s_bkgDT0QxNTQY07LnQRWQ?pwd=2dlb提取码:2dlb python3 VSCode 2.第一个爬虫的开发from urllib.request import urlopenurl = "http://www.baidu.com"resp = urlopen(url) #print(resp.read().decode(&q…

aardio 两行代码 优雅调用 libxl

废话不多说, 先给资源 https://files.cnblogs.com/files/blogs/762462/libxl.7z?t=1713539927&download=true 运行效果:文件存放路径:再上代码 import dotNet import consolexl = dotNet.load("libxl.net", "libxl.net.dll"); xl.import("libxl&…

c语言题目之求最大公约数

题目内容:求最大公约数 给定两个数,求这两个数的最大公约数 例如: 输入:20 40 输出:20 什么叫最大公约数? 方法分析: 提示:这里我们用辗转相除法: 例如:输…

信号处理相关知识

一: 1.序列——三种典型序列通过matlab绘图即可 2.数字信号的自变量一定是整数,幅度上取值是有限的状态(不一定是整数)。 3.抽取和插值 4.模拟正弦信号sin(wt):w是角频率,单位rad/s,f是频率w/2Π。 5.假设用采样周…

如何在Windows 10中启用和使用上帝模式,这里有详细步骤

序言 上帝模式(God Mode)是一个特殊的文件夹,只在一个窗口中显示所有可用的操作设置。它可以节省搜索命令的时间,而无需知道通过“开始”菜单或“控制面板”查找命令的步骤。上帝模式默认情况下是隐藏的,所以我们需要…

Godaddy ssl证书配置到nginx

godaddy官网:https://www.godaddy.com/ 由于godaddy下载的证书没有key文件,保存官方给的key文件nginx报错,所以先自己生成,重新在godaddy上申请验证.csr文件openssl req -new -newkey rsa:2048 -nodes -keyout domain.key -out domain.csr生成过程会询问几个常见问题,比如Ci…

1.微服务介绍

完整的微服务架构图 注册中心 配置中心 服务集群 服务网关 分布式缓存 分布式搜索 数据库集群 消息队列 分布式日志服务 系统监控链路追踪 Jenkins docker k8s 技术栈 微服务治理: 注册发现、远程调用、负载均衡、配置管理、网关路由、系统保护、流量…

华为云服务镜像手动更换

操作步骤: 1、进入华为云首页点击云容器引擎CCE; 2、选择你所要更换镜像的环境【这里以dev环境演示】; 3、点击dev环境后选择顶部的命名空间,点击【工作负载】中右侧栏的【升级】按钮; 4、点【更换镜像】选择你在test…

「探索C语言内存:动态内存管理解析」

🌠先赞后看,不足指正!🌠 🎈这将对我有很大的帮助!🎈 📝所属专栏:C语言知识 📝阿哇旭的主页:Awas-Home page 目录 引言 1. 静态内存 2. 动态内存 2.1 动态内…

【DL水记】循环神经网络RNN的前世今生,Transformer的崛起,Mamba模型

文章目录 RNN网络简介传统RNN网络结构RNN的分类 长-短期记忆网络 (LSTM)GRU网络横空出世的Transformer网络Self-AttentionVisionTransformer Mamba模型Reference: RNN网络简介 “当人类接触新事物时,他们不会从头开始思考。就像你在阅读这篇文章时,你会根…

【读点论文】YOLOX: Exceeding YOLO Series in 2021,无锚框单阶段目标检测方案,解耦检测头的分类和回归分支,优化标签分配策略

YOLOX: Exceeding YOLO Series in 2021 Abstract 在本报告中,我们介绍了YOLO系列的一些经验改进,形成了一种新的高性能探测器—YOLOX。我们将YOLO检测器切换到无锚方式,并进行其他先进的检测技术,即去耦头和领先的标签分配策略S…

常用UI组件

一、文本组件 1.1 概述 Text为文本组件,用于显示文字内容 1.2 参数 Text组件的参数类型为string | Resource Entry Component struct Index {build() {Column({space : 50}) {Text(你好).fontSize(50)}.width(100%).height(100%).justifyContent(FlexAlign.Cent…

【Entity Framework】闲话EF中批量配置

【Entity Framework】闲话EF中批量配置 文章目录 【Entity Framework】闲话EF中批量配置一、概述二、OnModelCreating中的批量配置元数据API的缺点 三、预先约定配置忽略类型默认类型映射预先约定配置的限制约定添加新约定替换现有约定约定实现注意事项 四、何时使用每种方法进…

Rust实现hotkey

最近想写一个快捷启动工具来满足自己的需求,刚好用自己最喜欢的 Rust 来实现实现方法 文档: global_hotkey 具体代码 use global_hotkey::{hotkey::{Code, HotKey, Modifiers},GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState, }; use winit::event_loop::{ControlFlow,…

2024面试软件测试,常见的面试题(上)

一、综合素质 1、自我介绍 面试官您好,我叫XXX,一直从事车载软件测试,负责最多的是中控方面。 以下是我的一些优势: 车载的测试流程我是熟练掌握的,且能够独立编写测试用例。 平时BUG提交会使用到Jira,类似…