注:亮度调节和音量调节gif无法体现,功能是ok的,其次默认Icon锁的close和open实在难以分辨。
环境:Flutter2.8.1channelstable;Dart2.15.1
需要音频播放器的看这里:Flutter音乐播放器
重点说下这个工具类,因为视频播放,涉及到状态改变有很多,笔者刚开始选择使用InheritedWidget来在众多的widget之间共享数据。但是总感觉这样有点繁琐,且不很优雅!
我们选择对第三方插件进行封装的目的不外乎这几个:
于是笔者就写了一个工具类VideoPlayerUtils,专门且只用来处理播放器的所有业务。包括暂停、播放、跳转、调节音量、调节亮度、切换视频等操作。在所有的widget中不会引用关于video_player或其他第三方插件的任何信息,VideoPlayerUtils负责widget与播放器之间的所有操作交互。后续优化迭代或更换播放器插件时,只需针对这个工具类进行修改,对所有widget不会有任何的影响,大大的解耦合了。
其中VideoPlayerState:
提供以上的公共属性,可以通过VideoPlayerUtils来获取对应的值,使用get只读,使外界不会误修改这些属性,以保证数值的安全性。开发者可根据自身需要自行添加属性。
提供以上方法来处理播放器的所有业务。同样的开发者可根据自身需要自行添加或修改。
重点说下这个方法,是整个业务的核心方法,控制视频的播放或暂停。开发者只要遇到播放或暂停是均可调用此方法,具体是播放或暂停,内部根据传入的url自行判断,开发者不需要关心。
切换新视频也是使用此方法,传入的url与上次不一致,自动切换新视频。笔者可根据statusListener来监听播放状态的改变,以此处理自身逻辑。
这个也需要提下,视频播放器在播放新视频时会异步初始化,一般我们的操作是在initState()初始化,成功后再setState()。这里笔者遇到一个让人蛋疼的问题:
我们看video_player的使用:
VideoPlayer(controller):widget中已经持有了controller。本来笔者封装的目的就是为了让widget与controller的之间解耦合。但此时的笔者。。。。
放弃不是不可能放弃的,这辈子都不会放弃的!
于是笔者取了巧,写了一个初始化监听器initializedListener,包换2个参数:bool,Widget,初始化是否成功;其中widget为初始化成功返回需要展示的播放器UI,失败默认返回constSizedBox()。
到这里就可以简单使用了:
没看错,视频播放就是这么简单。
如果有更多的业务功能,笔者也按照自己的需求写了一套,同样的开发者可根据自身需要自行添加或修改。
VideoPlayerGestures主要是处理手势的,比如快进、快退等跳转播放;左侧上下滑动调节亮度;右侧上下滑动调节音量;单击是否开启沉浸式播放,所有widget的隐藏与显示;双击播放、暂停等。
哦,还有PercentageWidget也放到这个文件下了,就是这玩意:
笔者处出于简单考虑,就按照整个UI的位置命名了。瞅一眼就知道是啥玩意。
同样的开发者可根据自身需要自行添加或修改。
就是这玩意:
同样的开发者可根据自身需要自行添加或修改。话说这个锁的Icon的open和close是真的难分辨!
这玩意是自定义的,别问,问就是跟产品干一架落了下风
主要就是自定义这玩意:
同样的开发者可根据自身需要自定义。
注:这里没有添加缓冲的进度,开发可查看video_player中的源码VideoProgressIndicator,按业务自行定义。
这玩意就是整合以上的widget,再考虑下全屏的安全区域,没啥东西。开发者可自行处理!
具体的实现监听器的思路,看这里。
自此一个漂亮的Flutter视频播放器就已经结束了。如果您觉得对您有些许帮助的话,欢迎Star!
一、产品定位:科技,智慧,时尚
One.Productlocation:technology,wisdom,fashion
incorporatingthethreeproductsofmobilephone,touchablewide-screenedipodandInternetCommunicationFacaltiesperfectly
iPhone引入了基于大型多触点显示屏和领先性新软件的全新用户界面,让用户用手指即可控制iPhone。
iPhonehasintroducedabrand-newUserInterfacewhichisbasedonthelarge-scalemulticonductorscreenandadvancedsoftware,thusitallowtheusertooperateiPhonebyfingers
二、消费者定位:高端人群
consumerlocation:high-endcomsumers
根据对产品特点的分析,我们将目标消费群定位高端人士。
Ontheanalysisoftheproducts'particularity,weintenditforthehigh-endconsumers
他们可能是公司管理者,因为事业需要,对于手机的功能有更高的要求。
Theymaybeacompany'smanager,becauseoftherequirementoftheirprofession,thayhavehigherdemandfortheirphone'sfunctions
他们也可能处于事业上升阶段,大业初成,高端手机是他们身份地位的象征。
Theymayalsobeinthestageofpromotionintheircareer,beforeachievinggreataccomplishments,high-endmobilephoneisthesymboloftheirstatus.
对于这一阶层的人群,Iphones既能满足他们对高端技术的实质需要,又能满足他们的心理需求。
topeopleofthisclass,iPhonecansatisfythepracticalneedofhightechnologytheyhaveaswellastheirpschologicaldemand.
为什么是uni-app
uni-app的组件有原生调用能力,第三方的vue库在调原生接口时跟5+runtime不兼容。就像nativescript有vue版和angular版,类似于reactnative,都是起源于phonegap/cordova
遵义小红椒建议
首先把flutter_native_splash导入到工程的pubspec.yaml中.这里需要注意的是需要放在dev_dependencies下,而不是dependencies.具体如下所示.
接下来我们就来配置flutter_native_splash,在配置之前我们看一下flutter_native_splash的可配置项.
例如,我现在只有一个logo图片,那么我想生成iOS和android两端的闪屏页,这时候我只需在pubspec.yaml如下设置即可.
当然了,如果你有其他配置可以自行进行添加.
配置完成了,我们该如何生成呢这时候需要我们打开终端cd到我们的工程目录下.如果是AndroidStudio或者VSCode默认就是在当前工程目录下.
然后我们需要执行下面的三个命令来生成闪屏页
每一次都敲三个命令实属麻烦,我们把上诉的三个命令整合成一个命令,如下所示.
那么,我们不想使用该插件生成的闪屏页该怎么办呢我们只需要执行下面命令即可.
注:每一次更换图片都是需要重新执行命令重新生成.
闲鱼的IM消息系统作为买家与卖家的沟通工具,增进理解、促进信任,对闲鱼的商品成交有重要的价值,是提升用户体验最关键的环节。
然而,随着业务体量的快速增长,当前这套消息系统正面临着诸多急待解决的问题。
以下几个问题典型最为典型:
1)在线消息的体验提升;
2)离线推送的到达率;
3)消息玩法与消息底层系统的耦合过强。
经过评估,我们认为现阶段离线推送的到达率问题最为关键,对用户体验影响较大。
(本文已同步发布于:)
从数据通信链接的技术角度,我们根据闲鱼客户端是否在线,将整体消息链路大致分为强感知链路和弱感知链路。
强感知链路由以下子系统或模块:
1)发送方客户端;
2)idleapi-message(闲鱼的消息网关);
3)heracles(闲鱼的消息底层服务);
4)accs(阿里自研的长连接通道);
5)接收方客户端组成。
整条链路的核心指标在于端到端延迟和消息到达率。
强感知链路中的双方都是在线的,消息到达客户端就可以保证接收方感知到。强感知链路的主要痛点在消息的端到端延迟。
弱感知链路与强感知链路的主要不同在于:弱感知链路的接收方是离线的,需要依赖离线推送这样的方式送达。
因此弱感知链路的用户感知度不强,其核心指标在于消息的到达率,而非延迟。
所以当前阶段,优化弱感知链路的重点也就是提升离线消息的到达率。换句话说,提升离线消息到达率问题,也就是优化弱感知链路本身。
下图一张整个IM消息系统的架构图,感受下整体链路:
如上图所示,各主要组件和子系统分工如下:
1)HSF是一个远程服务框架,是dubbo的内部版本;
2)tair是阿里自研的分布式缓存框架,支持memcached、Redis、LevelDB等不同存储引擎;
3)agoo是阿里的离线推送中台,负责整合不同厂商的离线推送通道,向集团用户提供一个统一的离线推送服务;
4)accs是阿里自研的长连接通道,为客户端、服务端的实时双向交互提供便利;
5)lindorm是阿里自研的NoSQL产品,与HBase有异曲同工之妙;
6)域环是闲鱼消息优化性能的核心结构,用来存储用户最新的若干条消息。
强感知链路和弱感知链路在通道选择上是不同的:
1)强感知链路使用accs这个在线通道;
2)弱感知链路使用agoo这个离线通道。
通俗了说,弱感知链路指的就是离线消息推送系统。
相比较于在线消息和端内推送(也就是上面说的强感知链路),离线推送难以确保被用户感知到。
典型的情况包括:
1)未发送到用户设备:即推送未送达用户设备,这种情况可以从通道的返回分析;
2)发送到用户设备但没有展示到系统通知栏:闲鱼曾遇到通道返回成功,但是用户未看到推送的案例;
3)展示到通知栏,并被系统折叠:不同安卓厂商对推送的折叠策略不同,被折叠后,需用户主动展开才能看到内容,触达效果明显变差;
4)展示到通知栏,并被用户忽略:离线推送的点击率相比于在线推送更低。
针对“1)未发送到用户设备”,原因有:
1)离线通道的token失效;
2)参数错误;
3)用户关闭应用通知;
4)用户已卸载等。
针对“3)展示到通知栏,并被系统折叠”,原因有:
1)通知的点击率;
2)应用在厂商处的权重;
3)推送的数量等。
针对“4)展示到通知栏,并被用户忽略”,原因有:
1)用户不愿意查看推送;
2)用户看到了推送,但是对内容不感兴趣;
3)用户在忙别的事,无暇处理。
总之:以上这些离线消息推送场景,对于用户来说感知度不高,我们也便称之为弱感知链路。
我们的弱感知链路分为3部分,即:
1)系统;
2)通道;
3)用户。
共包含了Hermes、agoo、厂商、设备、用户、承接页这几个环节。具体如下图所示。
从推送的产生到用户最终进入APP,共分为如下几个步骤:
步骤1:Hermes是闲鱼的用户触达系统,负责人群管理、内容管理、时机把控,是整个弱感知链路的起点。;
步骤2:agoo是阿里内部承接离线推送的中台,是闲鱼离线推送能力的基础;
步骤3:agoo实现离线推送依靠的是厂商的推送通道(如:苹果的apns通道、Google的fcm通道、及国内各厂商的自建通道。;
步骤4:通过厂商的通道,推送最终出现在用户的设备上,这是用户能感知到推送的前提条件;
步骤5:如果用户刚巧看到这条推送,推送的内容也很有趣,在用户的主动点击下会唤起APP,打开承接页,进而给用户展示个性化的商品。
经过以上5个步骤,至此弱感知链路就完成了使命。
弱感知链路的核心问题在于:
1)推送的消息是否投递给了用户;
2)已投递到的消息用户是否有感知。
这对应推送的两个阶段:
1)推送消息是否已到达设备;
2)用户是否查看推送并点击。
其中:到达设备这个阶段是最基础的,也是本次优化的核心。
我们可以将每一步的消息处理量依次平铺,展开为一张漏斗图,从而直观的查看链路的瓶颈。
漏斗图斜率最大的地方是优化的重点,差异小的地方不需要优化:
通过分析以上漏斗图,弱感知链路的优化重点在三个方面:
1)agoo受理率:是指我们发送推送请到的数量到可以通过agoo(阿里承接离线推送的中台)转发到厂商通道的数量之间的漏斗;
2)厂商受理率:是指agoo中台受理的量到厂商返回成功的量之间的漏斗;
3)Push点击率:也就通过以上通道最终已送到到用户终端的消息,是否最终转化为用户的主动“点击”。
有了优化方向,我们来看看优化手段吧。
跟随推送的视角,顺着链路看一下我们是如何进行优化的。
用户的推送,从Hermes站点搭乘“班车”,驶向下一站:agoo。
这是推送经历的第一站。到站一看,傻眼了,只有不到一半的推送到站下车了。这是咋回事嘞?
这就要先说说agoo了,调用agoo有两种方式:
1)指定设备和客户端,agoo直接将推送投递到相应的设备;
2)指定用户和客户端,agoo根据内部的转换表,找到用户对应的设备,再进行投递。
我们的系统不保存用户的设备信息。因此,是按照用户来调用agoo的。
同时:由于没有用户的设备信息,并不知道用户是iOS客户端还是Android客户端。工程侧不得不向iOS和Android都发送一遍推送。虽然保证了到达,但是,一半的调用都是无效的。
为了解这个问题:我们使用了agoo的设备信息。将用户转换设备这一阶段提前到了调用agoo之前,先明确用户对应的设备,再指定设备调用agoo,从而避免无效调用。
agoo调用方式优化后,立刻剔除了无效调用,agoo受理率有了明显提升。
至此:我们总算能对agoo受理失败的真正原因做一个高大上的分析了。
根据统计:推送被agoo拒绝的主要原因是——用户关闭了通知权限。同时,我们对agoo调用数据的进一步分析发现——有部分用户找不到对应的设备。优化到此,我们猛然发现多了两个问题。
那就继续优化呗:
1)通知体验优化,引导打开通知权限;
2)与agoo共建设备库,解决设备转换失败的问题。
这两个优化方向又是一片新天地,我们择日再聊。
推送到达agoo,分机型搭乘厂商“专列”,驶向下一站:用户设备。
这是推送经历的第二站。出站查票,发现竟然超员了。
于是乎:我们每天有大量推送因为超过厂商设定的限额被拦截。
为什么会这样呢?
实际上:提供推送通道的厂商(没错,各手机厂商的自家推送通道良莠不齐),为了保证用户体验,会对每个应用能够推送的消息总量进行限制。
对于厂商而言,这个限制会根据推送的类型和应用的用户规模设定——推送主要分为产品类的推送和营销类的推送。
厂商推送通道对于不同类型消息的限制是:
1)对于产品类推送,厂商会保证到达;
2)对于营销类推送,厂商会进行额度限制;
3)未标记的推送,默认作为营销类推送对待。
我们刚好没有对推送进行标记,因此触发了厂商的推送限制。
这对我们的用户来说,会带来困扰。闲鱼的交易,很依赖买卖家之间的消息互动。这部分消息是需要确保到达的。
根据主流厂商的接口协议,我们将推送的消息分为以下几类,并进行相应标记:
1)即时通讯消息;
2)订单状态变化;
4)营销消息这几类。
经过这些优化,因为超过厂商限额而被拦截的推送实现了清零。
通过优化agoo受理率、厂商受理率,我们解决了推送到达量的瓶颈。但即使消息被最终送达,用户到底点击了没有?这才是消息推送的根本意义所在。
于是,在日常的开发测试过程中,我们发现了推送的两个体验问题:
2)营销Push也有权限校验,更换用户登陆后无法点击。
针对Push的权限校验功能,闲鱼根据场景做了细分:
1)涉及个人隐私的推送,保持权限校验不变;
2)营销类的推送,放开权限校验。
以上是点击体验的优化,我们还需要考虑用户的点击意愿。
具体的优化手段是:
1)在推送内容上:我们需要优化的是推送的时机和相应的素材;
3)在推送素材上:算法会根据素材的实时点击反馈,对素材做实时赛马。只发用户感兴趣的素材,提高用户点击意愿。
通过以上我们的分析和技术优化手段,整体弱推送链路链路有了不错的提升,离线消息的到达率相对提升了两位数。
本篇主要和大家聊的是只是IM消息系统链路中的一环——弱感知链路的优化,落地到到具体的业务也就是离线消息送达率问题。
整体IM消息系统,还是一个比较复杂的领域。
我们在消息系统的发展过程中,面临着如下问题:
1)如何进行消息的链路追踪;
2)如何保证IM消息的快速到达(见《闲鱼亿级IM消息系统的及时性优化实践》);
3)如何将消息的玩法和底层能力分离;
4)离线推送中如何通过用户找到对应的设备。
[1]AndroidP正式版即将到来:后台应用保活、消息推送的真正噩梦
[2]一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践
[3]一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等
[4]一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等
[5]从新手到专家:如何设计一套亿级消息量的分布式IM系统
[8]移动端IM中大规模群消息的推送如何保证效率、实时性?
[9]现代IM系统中聊天消息的同步和存储方案探讨
[10]新手入门一篇就够:从零开发移动端IM
[11]移动端IM开发者必读(一):通俗易懂,理解移动网络的“弱”和“慢”
[12]移动端IM开发者必读(二):史上最全移动弱网络优化方法总结
[13]IM消息送达保证机制实现(一):保证在线实时消息的可靠投递
[14]IM消息送达保证机制实现(二):保证离线消息的可靠投递
[15]零基础IM开发入门(一):什么是IM系统?
[16]零基础IM开发入门(二):什么是IM系统的实时性?
[17]零基础IM开发入门(三):什么是IM系统的可靠性?
[18]零基础IM开发入门(四):什么是IM系统的消息时序一致性?