牛批!B站开源弹幕库无缝迁移到鸿蒙

百家 作者:51CTO技术栈 2021-02-26 14:25:27

基于Github 上哔哩哔哩开源项目(https://github.com/bilibili/DanmakuFlameMaster),进行了鸿蒙化迁移和重构,可实现弹幕的隐藏、显示、暂停、继续、发送、定时发送弹幕等一系列的功能。


代码已经开源 ,欢迎各位下载使用并提出宝贵意见:

https://gitee.com/isrc_ohos/danmaku-flame-master_ohos

为了更加清晰的介绍弹幕库的作用和使用方法,本文将从基础知识、使用方法、 sample 解析三个方面进行讲解。

01

弹幕库的基础知识


弹幕库是一个开源的三方库,在正式讲解弹幕库的使用之前,我们先来了解下第三方库的含义。


第三方库是指除本地类库、系统类库以外的类库,需要后来安装,才能调取使用。


简单来讲,相当于其他人写好了代码,实现了一些功能接口,我们直接调用这些接口,就可以实现一些我们想要的功能。


举个例子:在一个应用中实现视频缓存功能(断网后可继续播放提前缓存的内容),如果想要自己实现的话,需要调用鸿蒙/安卓底层的方法,代码量非常大。


如果已经有第三方库可以做到这点,并且开源了出来,那么你只需要引用他的第三方库,然后调用此库内公开的一些方法接口,简简单单的几行代码就可以实现缓存功能。


了解了什么是第三方库,现在我们来看弹幕库组件。看视频时,弹幕是必不可少的互动功能,这个功能就是调用了弹幕库组件来实现的。


现在我们已经实现了弹幕库功能在鸿蒙平台的构建,接下来我们一起深入了解下鸿蒙平台的弹幕库吧!


02

弹幕库的使用方法


关于弹幕库第三方组件的使用方法,我们准备了视频教学,大家可以下载使用哦!


视频教学版链接:
https://www.bilibili.com/video/BV1Dy4y1i7cG/

03

sample 解析


导入弹幕库组件后,其整个项目的文件结构如下:

整个项目有 2 个 module:entry 和 DanmakuFlameMaster。


在这里大家可以简单的理解:

  • “DanmakuFlameMaster”就是之前提到的【第三方库】,它里面实现了弹幕功能的 所有细节。

  • entry 就是一个具体的“客户端”module,它声明了在鸿蒙程序中的所有运行细节。

  • 最后跑起来的程序就是 entry,entry 里面实现了所有功能,包括界面、按钮、功能等等。

    只不过 entry 调用了第三方库“DanmakuFlameMaster”,使用了它的能力来实现了一些弹幕相关的功能。


本篇文章主要介绍如何在已经构建好了“DanmakuFlameMaster”这个第三方库的基础上,来进行“entry”的开发。


为了节省篇幅,此处我们使用一个已经开发完成了的 sample,接下来以这个 sample 为例,向大家讲解弹幕库组件的应用及效果。

将 entry 展开之后,我们可以看到,真正的代码是真的很少,主要的功能都集中在 MainAbility 和 MainAbilitySlice 文件中,加起来的代码量也就不到 1000 行。


接下面我们对这两个文件进行逐个解析:


①MainAbility,它是一切应用的入口。


根据鸿蒙能力,这里是可以通过 setMainRoute() 方法调用很多的 Slice 来进行功能的实现,上图标记的代码就是声明了 MainRoute 在 MainAbilitySlice 之中。


②MainAbilitySlice,这个文件比较重要。


这个文件主要包括:import 文件、初始化函数、功能实现函数三个部分。


(1)、import 文件


此处 import 的文件可以分为三类:SDK 文件、应用内文件和三方件文件。

  • SDK 文件:import 后路径为 ohos.....的。

  • 应用内文件:import 后路径为 com.huawei......的。

  • 三方库文件:import 后路径为 master.flame.danmuku......的。

(2)、初始化函数:

如图所示,初始化函数一共包含六个,我们将其分为五个部分,这五个部分与图中函数的顺序对应如下:UI 初始化、视频源的设置与播放(2 个函数)、输出系统内存信息、设置数据源(这里的数据指的是弹幕数据)以及弹幕输出。


这五个部分之中,我们主要讲解其中的与弹幕库密切相关的部分:UI 初始化、弹幕数据源与弹幕输出。


1)onStart 方法实现了 UI 初始化。

以上代码构建了弹幕库的显示窗口 设置了屏幕参数的读取 。


2)弹幕数据源和最终的弹幕输出。


这两个部分有强相关,我们放在一起来介绍:

第一步:声明全局变量 mParser 用来承载弹幕数据。

第二步:在加载数据源处调用了我们的第三方库的 BiliDanmukuParser 类,然后从我们的 resources 中读取弹幕数据。

可以看到在 datasource 其实就是读取了这里的文件,大家可以自己点进去看一下这个文件内容,里面就是我们的“弹幕数据库”。

第三步:将载入了弹幕的 BaseDanmakuParser 类对象,return 给全局变量 mParser(如果大家想找到这部分代码的话,可以到 findview() 中去寻找)。


③功能实现函数


在 UI 上可见的一些按钮,每个按钮都对应一个 callback 函数,点击按钮,执行对应的函数体,实现相关功能。

上图所示为点击不同的按钮,出现不同的弹幕效果,例如:隐藏弹幕、显示弹幕、暂停弹幕等。


下面我们将以弹幕库为例,让大家了解开源三方件是如何从安卓迁移到鸿蒙的!


看完本篇内容,你不仅能够明白"我们是如何迁移弹幕库"的,还可以自己尝试迁移感兴趣的安卓开源三方件!


本次的内容包含两个部分,主要为大家讲解:开源三方件迁移的本质是什么、如何完成开源三方件的迁移。


04

开源三方件迁移的本质


三方件是一个向下使用安卓/鸿蒙的 SDK 方法,完成特定功能,向上为用户提供统一的接口,供用户调用的中间件。


想将一个安卓的三方件迁移至鸿蒙,要做的是:向上的接口保持不变(功能输出和接口相同,方便用户调用),向下调用的方法由安卓转为鸿蒙,也就是从使用安卓的底层 SDK 提供的方法变为使用鸿蒙的底层 SDK 提供的方法。


下图以弹幕库为例子解释了这个过程:


图 1:弹幕库组件迁移示意图


在原始弹幕库组件版本中:
https://github.com/bilibili/DanmakuFlameMaster

我们点开其中任意几个文件,查看其 import 信息:

图 2:DrawHandler.java


可以看到,文件中使用了大量的"import android.xxxx"的包名,表明组件包含了大量安卓自身的 API 调用,来实现特定功能。


因此我们要做的是通过调用鸿蒙的 API 来实现相同的功能,由于安卓和鸿蒙底层架构及通信机制等各方面的不同,所以此处并不是简单地逐一替换关系。


05

开源三方件迁移的方法


在实际的迁移过程中,安卓和鸿蒙的包名、类名和方法名都可能会不同。根据迁移的难易程度,可以分为以下四种情况。


①包名不同,类名相同


有一些比较简单的类,它在安卓和鸿蒙中仅仅是包名发生了变化,类名、接口和方法没有变化,像这样的类,我们只要在文件中进行 import 后的包名替换即可,如下图所示。

图 3:源码示例


②类名不同,方法名相同


有一些类,它在安卓和鸿蒙中包名和类名都发生了变化,接口没有变化,如下图所示。

图 4:源码示例


此时需要翻阅安卓和鸿蒙的开发文档,以未变化的方法名为线索进行查找,对同功能的类进行匹配。


安卓:

https://developer.android.google.cn/reference/packages

鸿蒙:
https://developer.harmonyos.com/cn/docs/documentation/doc-references/overview-0000001054518434

像上面说的两种简单情况,在实际的迁移过程中几乎是不会出现的,在鸿蒙系统中也只有很少一部分基础库所提供的接口是与安卓相似的,其他部分的库与安卓相差甚远。


③包名、类名和方法完全不同


此时,对于安卓中调用的接口,无法直接在鸿蒙中找到与之对应的。需要我们具有一定的源码阅读能力,充分理解安卓接口的功能实现和模块划分(媒体、权限、视图...),再去鸿蒙相应的模块查找与之相似的使用方法。

图 5:源码示例


这里就是一个典型的例子,注释方法为安卓方法,下面方法是鸿蒙方法。可以看到在 RECT(Class:RectFloat)这个变量的初始化中就出现了不一样的使用方式。


④功能重写


前三种方法,接口在安卓和鸿蒙间存在对应关系,我们只需要理解、查找和匹配。


在功能实现方式完全不同的情况下,需要我们具有一定的代码阅读能力和重写能力,在鸿蒙中对相应功能进行重写。


以弹幕库为例:


多线程通信:安卓和鸿蒙的多线程通信机制类似,但是使用方法相差较大。


解决方法:功能重写。
图 6:安卓中的多线程通信逻辑

图 7:鸿蒙中的多线程通信逻辑


可以看到两个版本的多线程通信的大体逻辑都是相同的,两个 Thread 想要进行通信,都需要四个步骤:

  • 通信发起方需要构建(或实例化)一个结构体(或类)来进行事件的传递(并携带信息本身)。

  • 这个携带着信息的结构化对象到了信息接收方后,会首先进入一个消息队列进行等待处理。

  • 当结构化对象被从队列中取出后,会进入消息接收方的信息处理逻辑。

  • 处理完成后消息接收方会生成一个反馈(回调),将处理结果反馈给消息发送方。


但是我们可以细心观察一下,安卓和鸿蒙的处理细节是有很大差异的。


在安卓上使用这个机制的时候,需要先 new 一个 HandlerThread,然后 new 一个 looper,接着将他们绑定在一起,使用 Thread.start 才能让跑起来,实现消息队列发送及取出消息处理的功能。


在鸿蒙上则不用如此麻烦,只需要 new 一个 eventrunner 就可以实现同样的功能,如下图所示:

 图 8:源码示例


UI 部分:由于鸿蒙 UI 的构建方式和安卓存在很多差异,导致无法直接通过替换接口的方式来实现与安卓相对应的 UI 能力,这就需要进一步将对应的功能基于鸿蒙的构建方式进行重写。


解决方法:功能重写。


以 OHOSP/AOSP 自定义绘制能力对比为例:

图 9:OHOSP/AOSP 自定义绘制能力对比图


安卓在绘制时,直接调用 draw() 方法,将 canvas 绘制到 view 上,鸿蒙则需对当前的 component 添加 drawtask,重写 drawtask 里的 ondraw 方法,完成对 canvas 的绘制。


安卓实现如下:

图 10:安卓实现自定义绘制能力


鸿蒙:

图 11:鸿蒙实现自定义绘制能力


项目贡献人:熊轶翔、郑森文朱伟陈美汝吕泽


关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接